Browse Source

Merge com o master

Please enter a commit message to explain why this merge is necessary,
pull/713/head
Eduardo Calil 8 years ago
parent
commit
f1c528162c
  1. 14
      sapl/base/models.py
  2. 10
      sapl/base/templatetags/common_tags.py
  3. 73
      sapl/base/templatetags/menus.py
  4. 2
      sapl/base/urls.py
  5. 2
      sapl/base/views.py
  6. 11
      sapl/comissoes/views.py
  7. 2
      sapl/crispy_layout_mixin.py
  8. 62
      sapl/crud/base.py
  9. 33
      sapl/materia/forms.py
  10. 19
      sapl/materia/migrations/0051_auto_20161004_0927.py
  11. 20
      sapl/materia/migrations/0052_auto_20161004_1041.py
  12. 4
      sapl/materia/models.py
  13. 55
      sapl/materia/views.py
  14. 3
      sapl/norma/models.py
  15. 8
      sapl/norma/views.py
  16. 4
      sapl/parlamentares/views.py
  17. 18
      sapl/relatorios/urls.py
  18. 25
      sapl/sessao/migrations/0028_auto_20161004_0927.py
  19. 35
      sapl/sessao/migrations/0029_auto_20161004_1101.py
  20. 2
      sapl/sessao/models.py
  21. 257
      sapl/sessao/views.py
  22. 10
      sapl/templates/comissoes/materias_em_tramitacao.html
  23. 58
      sapl/templates/crud/detail.html
  24. 104
      sapl/templates/materia/materialegislativa_filter.html
  25. 6
      sapl/templates/materia/subnav.yaml
  26. 82
      sapl/templates/menus/mesa_diretora/mesa_diretora.html
  27. 8
      sapl/templates/menus/subnav.html
  28. 1
      sapl/templates/parlamentares/subnav.yaml
  29. 73
      sapl/templates/protocoloadm/documentoadministrativo_filter.html
  30. 95
      sapl/templates/protocoloadm/protocolo_filter.html
  31. 1
      sapl/templates/sessao/expedientemateria_form.html
  32. 6
      sapl/templates/sessao/layouts.yaml
  33. 8
      sapl/templates/sessao/painel.html
  34. 63
      sapl/templates/sessao/pauta_sessao_filter.html
  35. 6
      sapl/templates/sessao/resumo.html
  36. 13
      sapl/templates/sessao/sessaoplenaria_filter.html
  37. 5
      sapl/templates/sessao/subnav.yaml
  38. 22
      sapl/utils.py
  39. 2
      scripts/inicializa_grupos_autorizacoes.py
  40. 74
      scripts/test_inicializa_grupos_autorizacoes.py

14
sapl/base/models.py

@ -7,12 +7,11 @@ from django.contrib.contenttypes.models import ContentType
from django.core import exceptions from django.core import exceptions
from django.db import models, router from django.db import models, router
from django.db.utils import DEFAULT_DB_ALIAS from django.db.utils import DEFAULT_DB_ALIAS
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from sapl.utils import UF, YES_NO_CHOICES from sapl.utils import UF, YES_NO_CHOICES
TIPO_DOCUMENTO_ADMINISTRATIVO = (('O', _('Ostensivo')), TIPO_DOCUMENTO_ADMINISTRATIVO = (('O', _('Ostensivo')),
('R', _('Restritivo'))) ('R', _('Restritivo')))
@ -107,6 +106,15 @@ class AppConfig(models.Model):
('view_tabelas_auxiliares', _('Visualizar Tabelas Auxiliares')), ('view_tabelas_auxiliares', _('Visualizar Tabelas Auxiliares')),
) )
@classmethod
def attr(cls, attr):
config = AppConfig.objects.first()
if not config:
return ''
return getattr(config, attr)
def __str__(self): def __str__(self):
return _('Configurações da Aplicação - %(id)s') % { return _('Configurações da Aplicação - %(id)s') % {
'id': self.id} 'id': self.id}
@ -204,4 +212,4 @@ def create_proxy_permissions(
models.signals.post_migrate.connect( models.signals.post_migrate.connect(
receiver=create_proxy_permissions, receiver=create_proxy_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions") dispatch_uid="django.contrib.auth.management.create_permissions")

10
sapl/base/templatetags/common_tags.py

@ -122,10 +122,12 @@ def ultima_filiacao(value):
@register.filter @register.filter
def get_config_not_exists(user): def get_config_not_exists(user):
if not AppConfig.objects.all().exists(): return not AppConfig.objects.exists()
return True
else:
return False @register.filter
def get_config_attr(attribute):
return AppConfig.attr(attribute)
@register.filter @register.filter

73
sapl/base/templatetags/menus.py

@ -1,6 +1,7 @@
import yaml
from django import template from django import template
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
import yaml
register = template.Library() register = template.Library()
@ -14,6 +15,12 @@ def subnav(context, path=None):
1) Se a variável path não é nula; 1) Se a variável path não é nula;
2) Se existe no contexto a chave subnav_template_name; 2) Se existe no contexto a chave subnav_template_name;
3) o path default: <app_name>/subnav.yaml 3) o path default: <app_name>/subnav.yaml
Os campos esperados nos arquivos yaml são:
title
url
check_permission - opcional. quando usado
será realizado o teste de permissão para renderizá-lo.
""" """
menu = None menu = None
root_pk = context.get('root_pk', None) root_pk = context.get('root_pk', None)
@ -76,40 +83,36 @@ def resolve_urls_inplace(menu, pk, rm, context):
else: else:
if 'url' in menu: if 'url' in menu:
url_name = menu['url'] url_name = menu['url']
menu['url'] = reverse('%s:%s' % (rm.app_name, menu['url']),
kwargs={'pk': pk}) if 'check_permission' in menu and not context[
menu['active'] = 'active'\ 'request'].user.has_perm(menu['check_permission']):
if context['request'].path == menu['url'] else '' menu['url'] = ''
menu['active'] = ''
if not menu['active']: else:
""" menu['url'] = reverse(
Se não encontrada diretamente, '%s:%s' % (rm.app_name, menu['url']), kwargs={'pk': pk})
procura a url acionada dentro do crud, caso seja um. menu['active'] = 'active'\
Serve para manter o active no suvnav correto ao acionar if context['request'].path == menu['url'] else ''
as funcionalidades diretas do MasterDetailCrud, como:
- visualização de detalhes, adição, edição, remoção. if not menu['active']:
"""
Casos para urls_extras: Se não encontrada diretamente,
Em relações de segundo nível, como ocorre em procura a url acionada dentro do crud, caso seja um.
(0) Comissões -> (1) Composição -> (2) Participação Serve para manter o active no suvnav correto ao acionar
as funcionalidades diretas do MasterDetailCrud, como:
(2) não tem ligação direta com (1) através da view. Para (2) - visualização de detalhes, adição, edição, remoção.
ser localizado, e o nav-tabs ou nav-pills do front-end serem """
ativados foi inserido o teste de existência de urls_extras
para serem testadas e, sendo válidado, o active do front-end view = context['view']
seja devidamente colocado. if hasattr(view, '__class__') and\
""" hasattr(view.__class__, 'crud'):
urls = view.__class__.crud.get_urls()
view = context['view'] for u in urls:
if hasattr(view, '__class__') and\ if (u.name == url_name or
hasattr(view.__class__, 'crud'): 'urls_extras' in menu and
urls = view.__class__.crud.get_urls() u.name in menu['urls_extras']):
for u in urls: menu['active'] = 'active'
if (u.name == url_name or break
'urls_extras' in menu and
u.name in menu['urls_extras']):
menu['active'] = 'active'
break
if 'children' in menu: if 'children' in menu:
menu['active'] = resolve_urls_inplace( menu['active'] = resolve_urls_inplace(

2
sapl/base/urls.py

@ -20,7 +20,7 @@ urlpatterns = [
(TemplateView.as_view(template_name='sistema.html'))), (TemplateView.as_view(template_name='sistema.html'))),
url(r'^ajuda/', TemplateView.as_view(template_name='ajuda.html')), url(r'^ajuda/', TemplateView.as_view(template_name='ajuda.html')),
url(r'^relatorios/', TemplateView.as_view( url(r'^relatorios/$', TemplateView.as_view(
template_name='base/relatorios_list.html')), template_name='base/relatorios_list.html')),
url(r'^ajuda/(?P<topic>\w+)$', HelpView.as_view(), name='help_topic'), url(r'^ajuda/(?P<topic>\w+)$', HelpView.as_view(), name='help_topic'),
url(r'^ajuda/', TemplateView.as_view(template_name='ajuda/index.html'), url(r'^ajuda/', TemplateView.as_view(template_name='ajuda/index.html'),

2
sapl/base/views.py

@ -1,5 +1,5 @@
from braces.views import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import Count, Q from django.db.models import Count, Q
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect

11
sapl/comissoes/views.py

@ -68,13 +68,14 @@ class MateriasTramitacaoListView(ListView):
def get_queryset(self): def get_queryset(self):
# FIXME: Otimizar consulta # FIXME: Otimizar consulta
lista = [] lista = []
materias = MateriaLegislativa.objects.filter(tramitacao__isnull=False) materias = MateriaLegislativa.objects.filter(
tramitacao__isnull=False).order_by('tipo', 'ano', 'numero')
for materia in materias: for materia in materias:
comissao = materia.tramitacao_set.last( comissao = materia.tramitacao_set.last(
).unidade_tramitacao_local.comissao ).unidade_tramitacao_destino.comissao
if comissao: if (comissao and materia not in lista and
if comissao.pk == int(self.kwargs['pk']): comissao.pk == int(self.kwargs['pk'])):
lista.append(materia) lista.append(materia)
return lista return lista
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):

2
sapl/crispy_layout_mixin.py

@ -54,7 +54,7 @@ def get_field_display(obj, fieldname):
field = obj._meta.get_field(fieldname) field = obj._meta.get_field(fieldname)
except: except:
value = getattr(obj, fieldname) value = getattr(obj, fieldname)
return '', value return '', str(value)
verbose_name = str(field.verbose_name)\ verbose_name = str(field.verbose_name)\
if hasattr(field, 'verbose_name') else '' if hasattr(field, 'verbose_name') else ''
if hasattr(field, 'choices') and field.choices: if hasattr(field, 'choices') and field.choices:

62
sapl/crud/base.py

@ -13,8 +13,8 @@ from django.db import models
from django.http.response import Http404 from django.http.response import Http404
from django.utils.decorators import classonlymethod from django.utils.decorators import classonlymethod
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from django.views.generic import (CreateView, DeleteView, DetailView, ListView, from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
UpdateView) UpdateView)
from django.views.generic.base import ContextMixin from django.views.generic.base import ContextMixin
@ -23,7 +23,6 @@ from django.views.generic.list import MultipleObjectMixin
from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display
from sapl.utils import normalize from sapl.utils import normalize
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \ ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \
@ -158,11 +157,25 @@ class ListWithSearchForm(forms.Form):
) )
class PermissionRequiredForAppCrudMixin(PermissionRequiredMixin):
def has_permission(self):
apps = self.app_label
if isinstance(apps, str):
apps = apps,
# papp_label vazio dará acesso geral
for app in apps:
if not self.request.user.has_module_perms(app):
return False
return True
class PermissionRequiredContainerCrudMixin(PermissionRequiredMixin): class PermissionRequiredContainerCrudMixin(PermissionRequiredMixin):
def has_permission(self): def has_permission(self):
perms = self.get_permission_required() perms = self.get_permission_required()
# Torna a view pública se não possuir o atributo permission_required # Torna a view pública se não possuir conteudo
# no atributo permission_required
return self.request.user.has_perms(perms) if len(perms) else True return self.request.user.has_perms(perms) if len(perms) else True
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
@ -302,7 +315,7 @@ class CrudBaseMixin(CrispyLayoutFormMixin):
def delete_url(self): def delete_url(self):
obj = self.crud if hasattr(self, 'crud') else self obj = self.crud if hasattr(self, 'crud') else self
if not obj.DeleteView.permission_required: if not obj.DeleteView.permission_required:
return self.resolve_url(ACTION_DELETE) return self.resolve_url(ACTION_DELETE, args=(self.object.id,))
else: else:
return self.resolve_url(ACTION_DELETE, args=(self.object.id,))\ return self.resolve_url(ACTION_DELETE, args=(self.object.id,))\
if self.request.user.has_perm( if self.request.user.has_perm(
@ -354,7 +367,6 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
for fieldname in self.list_field_names: for fieldname in self.list_field_names:
if not isinstance(fieldname, tuple): if not isinstance(fieldname, tuple):
fieldname = fieldname, fieldname = fieldname,
s = [] s = []
for fn in fieldname: for fn in fieldname:
m = self.model m = self.model
@ -363,20 +375,12 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
f = m._meta.get_field(f) f = m._meta.get_field(f)
if hasattr(f, 'related_model') and f.related_model: if hasattr(f, 'related_model') and f.related_model:
m = f.related_model m = f.related_model
if m == self.model: s.append(force_text(f.verbose_name))
s.append(force_text(f.verbose_name))
else:
s.append(force_text(m._meta.verbose_name))
s = ' / '.join(s) s = ' / '.join(s)
r.append(s) r.append(s)
return r return r
def _as_row(self, obj): def _as_row(self, obj):
"""
FIXME: Refatorar função para capturar url correta em caso de uso de
campos foreignkey. getHeaders faz isso para construir o título.
falta fazer com esta função
"""
r = [] r = []
for i, name in enumerate(self.list_field_names): for i, name in enumerate(self.list_field_names):
url = self.resolve_url( url = self.resolve_url(
@ -388,20 +392,28 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
url = url + ('?pkk=' + self.kwargs['pk'] url = url + ('?pkk=' + self.kwargs['pk']
if 'pk' in self.kwargs else '') if 'pk' in self.kwargs else '')
if not isinstance(name, tuple):
name = name,
""" se elemento de list_field_name for uma tupla, constrói a """ se elemento de list_field_name for uma tupla, constrói a
informação com ' - ' se os campos forem simples, informação com ' - ' se os campos forem simples,
ou com <br> se for m2m """ ou com <br> se for m2m """
if isinstance(name, tuple): if isinstance(name, tuple):
s = '' s = ''
for j, n in enumerate(name): for j, n in enumerate(name):
ss = get_field_display(obj, n)[1] m = obj
ss = ( n = n.split('__')
('<br>' if '<ul>' in ss else ' - ') + ss)\ for f in n[:-1]:
if ss and j != 0 and s else ss m = getattr(m, f)
s += ss if not m:
break
if m:
ss = get_field_display(m, n[-1])[1]
ss = (
('<br>' if '<ul>' in ss else ' - ') + ss)\
if ss and j != 0 and s else ss
s += ss
r.append((s, url)) r.append((s, url))
else:
r.append((get_field_display(obj, name)[1], url))
return r return r
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -674,7 +686,10 @@ class CrudDetailView(PermissionRequiredContainerCrudMixin,
return DetailView.get_object(self, queryset=queryset) return DetailView.get_object(self, queryset=queryset)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.model.objects.get(pk=kwargs.get('pk')) try:
self.object = self.model.objects.get(pk=kwargs.get('pk'))
except:
raise Http404
obj = self.crud if hasattr(self, 'crud') else self obj = self.crud if hasattr(self, 'crud') else self
if hasattr(obj, 'model_set') and obj.model_set: if hasattr(obj, 'model_set') and obj.model_set:
self.object_list = self.get_queryset() self.object_list = self.get_queryset()
@ -1070,9 +1085,6 @@ class MasterDetailCrud(Crud):
def get_url_regex(cls): def get_url_regex(cls):
return r'^%s/(?P<pk>\d+)/edit$' % cls.model._meta.model_name return r'^%s/(?P<pk>\d+)/edit$' % cls.model._meta.model_name
def get(self, request, *args, **kwargs):
return Crud.UpdateView.get(self, request, *args, **kwargs)
class DeleteView(Crud.DeleteView): class DeleteView(Crud.DeleteView):
permission_required = RP_DELETE, permission_required = RP_DELETE,

33
sapl/materia/forms.py

@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
import django_filters
from crispy_forms.helper import FormHelper 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
from django import forms from django import forms
@ -11,7 +12,6 @@ from django.db import models, transaction
from django.db.models import Max from django.db.models import Max
from django.forms import ModelForm from django.forms import ModelForm
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import django_filters
from sapl.comissoes.models import Comissao 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
@ -27,7 +27,6 @@ from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria,
Numeracao, Proposicao, Relatoria, TipoMateriaLegislativa, Numeracao, Proposicao, Relatoria, TipoMateriaLegislativa,
Tramitacao, UnidadeTramitacao) Tramitacao, UnidadeTramitacao)
ANO_CHOICES = [('', '---------')] + RANGE_ANOS ANO_CHOICES = [('', '---------')] + RANGE_ANOS
@ -364,17 +363,35 @@ class LegislacaoCitadaForm(ModelForm):
else: else:
cleaned_data['norma'] = norma cleaned_data['norma'] = norma
if LegislacaoCitada.objects.filter( filtro_base = LegislacaoCitada.objects.filter(
materia=self.instance.materia, materia=self.instance.materia,
norma=cleaned_data['norma'] norma=self.cleaned_data['norma'],
).exists(): disposicoes=self.cleaned_data['disposicoes'],
msg = _('Essa Legislação já foi cadastrada.') parte=self.cleaned_data['parte'],
raise ValidationError(msg) livro=self.cleaned_data['livro'],
titulo=self.cleaned_data['titulo'],
capitulo=self.cleaned_data['capitulo'],
secao=self.cleaned_data['secao'],
subsecao=self.cleaned_data['subsecao'],
artigo=self.cleaned_data['artigo'],
paragrafo=self.cleaned_data['paragrafo'],
inciso=self.cleaned_data['inciso'],
alinea=self.cleaned_data['alinea'],
item=self.cleaned_data['item'])
if not self.instance.id:
if filtro_base.exists():
msg = _('Essa Legislação já foi cadastrada.')
raise ValidationError(msg)
else:
if filtro_base.exclude(id=self.instance.id).exists():
msg = _('Essa Legislação já foi cadastrada.')
raise ValidationError(msg)
return cleaned_data return cleaned_data
def save(self, commit=False): def save(self, commit=False):
legislacao = super(LegislacaoCitadaForm, self).save(commit) legislacao = super(LegislacaoCitadaForm, self).save(commit)
legislacao.norma = self.cleaned_data['norma'] legislacao.norma = self.cleaned_data['norma']
legislacao.save() legislacao.save()
return legislacao return legislacao

19
sapl/materia/migrations/0051_auto_20161004_0927.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-04 12:27
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0050_auto_20161003_0417'),
]
operations = [
migrations.AlterModelOptions(
name='tramitacao',
options={'ordering': ('-data_tramitacao',), 'verbose_name': 'Tramitação', 'verbose_name_plural': 'Tramitações'},
),
]

20
sapl/materia/migrations/0052_auto_20161004_1041.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-04 13:41
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0051_auto_20161004_0927'),
]
operations = [
migrations.AlterField(
model_name='regimetramitacao',
name='descricao',
field=models.CharField(max_length=50, verbose_name='Descrição'),
),
]

4
sapl/materia/models.py

@ -35,7 +35,7 @@ class TipoMateriaLegislativa(models.Model):
class RegimeTramitacao(models.Model): class RegimeTramitacao(models.Model):
descricao = models.CharField(max_length=50) descricao = models.CharField(max_length=50, verbose_name=_('Descrição'))
class Meta: class Meta:
verbose_name = _('Regime Tramitação') verbose_name = _('Regime Tramitação')
@ -589,7 +589,7 @@ class Tramitacao(models.Model):
class Meta: class Meta:
verbose_name = _('Tramitação') verbose_name = _('Tramitação')
verbose_name_plural = _('Tramitações') verbose_name_plural = _('Tramitações')
ordering = 'data_tramitacao', ordering = '-data_tramitacao',
def __str__(self): def __str__(self):
return _('%(materia)s | %(status)s | %(data)s') % { return _('%(materia)s | %(status)s | %(data)s') % {

55
sapl/materia/views.py

@ -26,9 +26,11 @@ from django_filters.views import FilterView
from sapl.base.models import CasaLegislativa from sapl.base.models import CasaLegislativa
from sapl.compilacao.views import IntegracaoTaView from sapl.compilacao.views import IntegracaoTaView
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux, CrudDetailView, from sapl.crud.base import (ACTION_CREATE, ACTION_DELETE, ACTION_DETAIL,
MasterDetailCrud, make_pagination) ACTION_LIST, ACTION_UPDATE, RP_DETAIL, RP_LIST,
from sapl.materia.forms import AnexadaForm Crud, CrudAux, CrudDetailView, MasterDetailCrud,
make_pagination)
from sapl.materia.forms import AnexadaForm, LegislacaoCitadaForm
from sapl.norma.models import LegislacaoCitada from sapl.norma.models import LegislacaoCitada
from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label, from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label,
autor_modal, gerar_hash_arquivo, get_base_url, autor_modal, gerar_hash_arquivo, get_base_url,
@ -50,9 +52,6 @@ from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria,
TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao, TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao,
Tramitacao, UnidadeTramitacao) Tramitacao, UnidadeTramitacao)
AnexadaCrud = Crud.build(Anexada, '')
OrigemCrud = Crud.build(Origem, '') OrigemCrud = Crud.build(Origem, '')
TipoMateriaCrud = CrudAux.build( TipoMateriaCrud = CrudAux.build(
@ -720,7 +719,39 @@ class LegislacaoCitadaCrud(MasterDetailCrud):
return reverse('%s:%s' % (namespace, self.url_name(suffix)), return reverse('%s:%s' % (namespace, self.url_name(suffix)),
args=args) args=args)
def has_permission(self):
return self.request.user.has_module_perms('materia')
@property
def list_url(self):
return self.resolve_url(ACTION_LIST, args=(self.kwargs['pk'],))\
if self.request.user.has_module_perms('materia') else ''
@property
def create_url(self):
return self.resolve_url(ACTION_CREATE, args=(self.kwargs['pk'],))\
if self.request.user.has_module_perms('materia') else ''
@property
def detail_url(self):
return self.resolve_url(ACTION_DETAIL, args=(self.object.id,))\
if self.request.user.has_module_perms('materia') else ''
@property
def update_url(self):
return self.resolve_url(ACTION_UPDATE, args=(self.kwargs['pk'],))\
if self.request.user.has_module_perms('materia') else ''
@property
def delete_url(self):
return self.resolve_url(ACTION_DELETE, args=(self.object.id,))\
if self.request.user.has_module_perms('materia') else ''
class CreateView(MasterDetailCrud.CreateView):
form_class = LegislacaoCitadaForm
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = LegislacaoCitadaForm
def get_initial(self): def get_initial(self):
self.initial['tipo'] = self.object.norma.tipo.id self.initial['tipo'] = self.object.norma.tipo.id
@ -751,6 +782,18 @@ class AnexadaCrud(MasterDetailCrud):
class BaseMixin(MasterDetailCrud.BaseMixin): class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['materia_anexada', 'data_anexacao'] list_field_names = ['materia_anexada', 'data_anexacao']
class CreateView(MasterDetailCrud.CreateView):
form_class = AnexadaForm
class UpdateView(MasterDetailCrud.UpdateView):
form_class = AnexadaForm
def get_initial(self):
self.initial['tipo'] = self.object.materia_anexada.tipo.id
self.initial['numero'] = self.object.materia_anexada.numero
self.initial['ano'] = self.object.materia_anexada.ano
return self.initial
class DetailView(MasterDetailCrud.DetailView): class DetailView(MasterDetailCrud.DetailView):
@property @property

3
sapl/norma/models.py

@ -167,6 +167,9 @@ class LegislacaoCitada(models.Model):
verbose_name = _('Legislação') verbose_name = _('Legislação')
verbose_name_plural = _('Legislações') verbose_name_plural = _('Legislações')
def __str__(self):
return str(self.norma)
class VinculoNormaJuridica(models.Model): class VinculoNormaJuridica(models.Model):
TIPO_VINCULO_CHOICES = ( TIPO_VINCULO_CHOICES = (

8
sapl/norma/views.py

@ -5,12 +5,12 @@ from django.views.generic import FormView, ListView
from sapl.compilacao.views import IntegracaoTaView from sapl.compilacao.views import IntegracaoTaView
from sapl.crud.base import RP_DETAIL, RP_LIST, Crud, CrudAux, make_pagination from sapl.crud.base import RP_DETAIL, RP_LIST, Crud, CrudAux, make_pagination
from sapl.norma.forms import NormaJuridicaForm
from .forms import NormaJuridicaPesquisaForm from .forms import NormaJuridicaPesquisaForm
from .models import (AssuntoNorma, LegislacaoCitada, NormaJuridica, from .models import AssuntoNorma, NormaJuridica, TipoNormaJuridica
TipoNormaJuridica)
LegislacaoCitadaCrud = Crud.build(LegislacaoCitada, '') # LegislacaoCitadaCrud = Crud.build(LegislacaoCitada, '')
AssuntoNormaCrud = CrudAux.build(AssuntoNorma, 'assunto_norma_juridica', AssuntoNormaCrud = CrudAux.build(AssuntoNorma, 'assunto_norma_juridica',
@ -28,6 +28,7 @@ class NormaCrud(Crud):
public = [RP_LIST, RP_DETAIL] public = [RP_LIST, RP_DETAIL]
class UpdateView(Crud.UpdateView): class UpdateView(Crud.UpdateView):
form_class = NormaJuridicaForm
@property @property
def layout_key(self): def layout_key(self):
@ -42,6 +43,7 @@ class NormaCrud(Crud):
return self.initial.copy() return self.initial.copy()
class CreateView(Crud.CreateView): class CreateView(Crud.CreateView):
form_class = NormaJuridicaForm
@property @property
def layout_key(self): def layout_key(self):

4
sapl/parlamentares/views.py

@ -118,6 +118,10 @@ class MandatoCrud(MasterDetailCrud):
model = Mandato model = Mandato
parent_field = 'parlamentar' parent_field = 'parlamentar'
public = [RP_DETAIL, RP_LIST] public = [RP_DETAIL, RP_LIST]
list_field_names = ['legislatura',
'votos_recebidos',
'coligacao',
'coligacao__numero_votos']
class ListView(MasterDetailCrud.ListView): class ListView(MasterDetailCrud.ListView):
ordering = ('-legislatura__numero') ordering = ('-legislatura__numero')

18
sapl/relatorios/urls.py

@ -10,22 +10,22 @@ from .views import (relatorio_capa_processo,
app_name = AppConfig.name app_name = AppConfig.name
urlpatterns = [ urlpatterns = [
url(r'^relatorio/materia$', relatorio_materia, name='relatorio_materia'), url(r'^relatorios/materia$', relatorio_materia, name='relatorio_materia'),
url(r'^relatorio/capa-processo$', url(r'^relatorios/capa-processo$',
relatorio_capa_processo, name='relatorio_capa_processo'), relatorio_capa_processo, name='relatorio_capa_processo'),
url(r'^relatorio/ordem-dia$', relatorio_ordem_dia, url(r'^relatorios/ordem-dia$', relatorio_ordem_dia,
name='relatorio_ordem_dia'), name='relatorio_ordem_dia'),
url(r'^relatorio/relatorio-documento-administrativo$', url(r'^relatorios/relatorio-documento-administrativo$',
relatorio_documento_administrativo, relatorio_documento_administrativo,
name='relatorio_documento_administrativo'), name='relatorio_documento_administrativo'),
url(r'^relatorio/espelho$', relatorio_espelho, url(r'^relatorios/espelho$', relatorio_espelho,
name='relatorio_espelho'), name='relatorio_espelho'),
url(r'^relatorio/(?P<pk>\d+)/sessao-plenaria$', url(r'^relatorios/(?P<pk>\d+)/sessao-plenaria$',
relatorio_sessao_plenaria, name='relatorio_sessao_plenaria'), relatorio_sessao_plenaria, name='relatorio_sessao_plenaria'),
url(r'^relatorio/protocolo$', url(r'^relatorios/protocolo$',
relatorio_protocolo, name='relatorio_protocolo'), relatorio_protocolo, name='relatorio_protocolo'),
url(r'^relatorio/(?P<nro>\d+)/(?P<ano>\d+)/etiqueta-protocolo$', url(r'^relatorios/(?P<nro>\d+)/(?P<ano>\d+)/etiqueta-protocolo$',
relatorio_etiqueta_protocolo, name='relatorio_etiqueta_protocolo'), relatorio_etiqueta_protocolo, name='relatorio_etiqueta_protocolo'),
url(r'^relatorio/pauta-sessao$', url(r'^relatorios/pauta-sessao$',
relatorio_pauta_sessao, name='relatorio_pauta_sessao'), relatorio_pauta_sessao, name='relatorio_pauta_sessao'),
] ]

25
sapl/sessao/migrations/0028_auto_20161004_0927.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-04 12:27
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0027_auto_20161003_0417'),
]
operations = [
migrations.AlterField(
model_name='orador',
name='url_discurso',
field=models.CharField(blank=True, max_length=150, verbose_name='URL Vídeo ou Observação'),
),
migrations.AlterField(
model_name='oradorexpediente',
name='url_discurso',
field=models.CharField(blank=True, max_length=150, verbose_name='URL Vídeo ou Observação'),
),
]

35
sapl/sessao/migrations/0029_auto_20161004_1101.py

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-04 14:01
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0028_auto_20161004_0927'),
]
operations = [
migrations.AddField(
model_name='orador',
name='observacao',
field=models.CharField(blank=True, max_length=150, verbose_name='Observação'),
),
migrations.AddField(
model_name='oradorexpediente',
name='observacao',
field=models.CharField(blank=True, max_length=150, verbose_name='Observação'),
),
migrations.AlterField(
model_name='orador',
name='url_discurso',
field=models.URLField(blank=True, max_length=150, verbose_name='URL Vídeo'),
),
migrations.AlterField(
model_name='oradorexpediente',
name='url_discurso',
field=models.URLField(blank=True, max_length=150, verbose_name='URL Vídeo'),
),
]

2
sapl/sessao/models.py

@ -215,6 +215,8 @@ class AbstractOrador(models.Model): # Oradores
verbose_name=_('Ordem de pronunciamento')) verbose_name=_('Ordem de pronunciamento'))
url_discurso = models.URLField( url_discurso = models.URLField(
max_length=150, blank=True, verbose_name=_('URL Vídeo')) max_length=150, blank=True, verbose_name=_('URL Vídeo'))
observacao = models.CharField(
max_length=150, blank=True, verbose_name=_('Observação'))
class Meta: class Meta:
abstract = True abstract = True

257
sapl/sessao/views.py

@ -1,9 +1,7 @@
from datetime import datetime from datetime import datetime
from re import sub from re import sub
from braces.views import PermissionRequiredMixin
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.forms.utils import ErrorList from django.forms.utils import ErrorList
@ -19,8 +17,10 @@ from django.views.generic.edit import FormMixin
from django_filters.views import FilterView from django_filters.views import FilterView
from rest_framework import generics from rest_framework import generics
from sapl.base.models import AppConfig as AppsAppConfig
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux, from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
MasterDetailCrud, make_pagination) MasterDetailCrud,
PermissionRequiredForAppCrudMixin, make_pagination)
from sapl.materia.forms import pega_ultima_tramitacao from sapl.materia.forms import pega_ultima_tramitacao
from sapl.materia.models import (Autoria, DocumentoAcessorio, from sapl.materia.models import (Autoria, DocumentoAcessorio,
TipoMateriaLegislativa, Tramitacao) TipoMateriaLegislativa, Tramitacao)
@ -29,8 +29,9 @@ from sapl.norma.models import NormaJuridica
from sapl.parlamentares.models import (Legislatura, Parlamentar, from sapl.parlamentares.models import (Legislatura, Parlamentar,
SessaoLegislativa) SessaoLegislativa)
from sapl.sessao.apps import AppConfig from sapl.sessao.apps import AppConfig
from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm
from sapl.sessao.serializers import SessaoPlenariaSerializer from sapl.sessao.serializers import SessaoPlenariaSerializer
from sapl.utils import permissoes_painel, permissoes_sessao from sapl.utils import permission_required_for_app
from .forms import (AdicionarVariasMateriasFilterSet, ExpedienteForm, from .forms import (AdicionarVariasMateriasFilterSet, ExpedienteForm,
ListMateriaForm, MesaForm, PautaSessaoFilterSet, ListMateriaForm, MesaForm, PautaSessaoFilterSet,
@ -43,9 +44,8 @@ from .models import (Bancada, Bloco, CargoBancada, CargoMesa,
SessaoPlenariaPresenca, TipoExpediente, SessaoPlenariaPresenca, TipoExpediente,
TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar) TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar)
OrdemDiaCrud = Crud.build(OrdemDia, '') # OrdemDiaCrud = Crud.build(OrdemDia, '')
RegistroVotacaoCrud = Crud.build(RegistroVotacao, '') # RegistroVotacaoCrud = Crud.build(RegistroVotacao, '')
TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria')
TipoExpedienteCrud = CrudAux.build(TipoExpediente, 'tipo_expediente') TipoExpedienteCrud = CrudAux.build(TipoExpediente, 'tipo_expediente')
CargoBancadaCrud = CrudAux.build(CargoBancada, '') CargoBancadaCrud = CrudAux.build(CargoBancada, '')
@ -122,18 +122,21 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
model = OrdemDia model = OrdemDia
parent_field = 'sessao_plenaria' parent_field = 'sessao_plenaria'
help_path = '' help_path = ''
public = [RP_LIST, RP_DETAIL]
class BaseMixin(MasterDetailCrud.BaseMixin): class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['numero_ordem', 'materia', 'observacao', list_field_names = ['numero_ordem', 'materia', 'observacao',
'resultado'] 'resultado']
class CreateView(MasterDetailCrud.CreateView): class CreateView(MasterDetailCrud.CreateView):
form_class = OrdemDiaForm
def get_success_url(self): def get_success_url(self):
return reverse('sapl.sessao:ordemdia_list', return reverse('sapl.sessao:ordemdia_list',
kwargs={'pk': self.kwargs['pk']}) kwargs={'pk': self.kwargs['pk']})
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = OrdemDiaForm
def get_initial(self): def get_initial(self):
self.initial['tipo_materia'] = self.object.materia.tipo.id self.initial['tipo_materia'] = self.object.materia.tipo.id
@ -173,11 +176,11 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
'pk': obj.sessao_plenaria_id, 'pk': obj.sessao_plenaria_id,
'oid': obj.materia_id, 'oid': obj.materia_id,
'mid': obj.pk}) 'mid': obj.pk})
if self.request.user.has_perms(permissoes_sessao()): if self.request.user.has_module_perms(AppConfig.label):
btn_registrar = ''' btn_registrar = '''
<a href="%s" <a href="%s"
class="btn btn-primary" class="btn btn-primary"
role="button">Registrar Votação</a>''' % ( role="button">Registrar Votação</a>''' % (
url) url)
obj.resultado = btn_registrar obj.resultado = btn_registrar
else: else:
@ -245,12 +248,13 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
model = ExpedienteMateria model = ExpedienteMateria
parent_field = 'sessao_plenaria' parent_field = 'sessao_plenaria'
help_path = '' help_path = ''
public = [RP_LIST, RP_DETAIL]
class BaseMixin(MasterDetailCrud.BaseMixin): class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['numero_ordem', 'materia', list_field_names = ['numero_ordem', 'materia',
'observacao', 'resultado'] 'observacao', 'resultado']
class ListView(Crud.PublicMixin, MasterDetailCrud.ListView): class ListView(MasterDetailCrud.ListView):
ordering = ['numero_ordem', 'materia', 'resultado'] ordering = ['numero_ordem', 'materia', 'resultado']
def get_rows(self, object_list): def get_rows(self, object_list):
@ -277,16 +281,19 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
'oid': obj.materia_id, 'oid': obj.materia_id,
'mid': obj.pk}) 'mid': obj.pk})
btn_registrar = ''' if self.request.user.has_module_perms(AppConfig.label):
<a href="%s" btn_registrar = '''
class="btn btn-primary" <a href="%s" class="btn btn-primary"
role="button">Registrar Votação</a>''' % (url) role="button">
obj.resultado = btn_registrar Registrar Votação</a>''' % (url)
obj.resultado = btn_registrar
else: else:
url = reverse('sapl.sessao:abrir_votacao_exp', kwargs={ url = reverse('sapl.sessao:abrir_votacao_exp', kwargs={
'pk': obj.pk, 'spk': obj.sessao_plenaria_id}) 'pk': obj.pk, 'spk': obj.sessao_plenaria_id})
btn_abrir = ''' btn_abrir = '''Matéria não votada<br />'''
Matéria não votada<br />
if self.request.user.has_module_perms(AppConfig.label):
btn_abrir += '''
<a href="%s" <a href="%s"
class="btn btn-primary" class="btn btn-primary"
role="button">Abrir Votação</a>''' % (url) role="button">Abrir Votação</a>''' % (url)
@ -294,35 +301,40 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
obj.resultado = btn_abrir obj.resultado = btn_abrir
else: else:
url = '' url = ''
if obj.tipo_votacao == 1:
url = reverse('sapl.sessao:votacaosimbolicaexpedit', if self.request.user.has_module_perms(AppConfig.label):
kwargs={ if obj.tipo_votacao == 1:
'pk': obj.sessao_plenaria_id, url = reverse(
'oid': obj.materia_id, 'sapl.sessao:votacaosimbolicaexpedit',
'mid': obj.pk}) kwargs={
elif obj.tipo_votacao == 2: 'pk': obj.sessao_plenaria_id,
url = reverse('sapl.sessao:votacaonominalexpedit', 'oid': obj.materia_id,
kwargs={ 'mid': obj.pk})
'pk': obj.sessao_plenaria_id, elif obj.tipo_votacao == 2:
'oid': obj.materia_id, url = reverse('sapl.sessao:votacaonominalexpedit',
'mid': obj.pk}) kwargs={
elif obj.tipo_votacao == 3: 'pk': obj.sessao_plenaria_id,
url = reverse('sapl.sessao:votacaosecretaexpedit', 'oid': obj.materia_id,
kwargs={ 'mid': obj.pk})
'pk': obj.sessao_plenaria_id, elif obj.tipo_votacao == 3:
'oid': obj.materia_id, url = reverse('sapl.sessao:votacaosecretaexpedit',
'mid': obj.pk}) kwargs={
obj.resultado = '<a href="%s">%s</a>' % (url, 'pk': obj.sessao_plenaria_id,
obj.resultado) 'oid': obj.materia_id,
'mid': obj.pk})
obj.resultado = '<a href="%s">%s</a>' % (url,
obj.resultado)
return [self._as_row(obj) for obj in object_list] return [self._as_row(obj) for obj in object_list]
class CreateView(MasterDetailCrud.CreateView): class CreateView(MasterDetailCrud.CreateView):
form_class = ExpedienteMateriaForm
def get_success_url(self): def get_success_url(self):
return reverse('sapl.sessao:expedientemateria_list', return reverse('sapl.sessao:expedientemateria_list',
kwargs={'pk': self.kwargs['pk']}) kwargs={'pk': self.kwargs['pk']})
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = ExpedienteMateriaForm
def get_initial(self): def get_initial(self):
self.initial['tipo_materia'] = self.object.materia.tipo.id self.initial['tipo_materia'] = self.object.materia.tipo.id
@ -403,9 +415,6 @@ class SessaoCrud(Crud):
list_field_names = ['data_inicio', 'legislatura', 'sessao_legislativa', list_field_names = ['data_inicio', 'legislatura', 'sessao_legislativa',
'tipo'] 'tipo']
class CrudDetailView(DetailView):
model = SessaoPlenaria
class ListView(Crud.ListView): class ListView(Crud.ListView):
ordering = ['-data_inicio'] ordering = ['-data_inicio']
@ -419,6 +428,13 @@ class SessaoCrud(Crud):
'sessao_legislativa': sessao_legislativa} 'sessao_legislativa': sessao_legislativa}
class SessaoPermissionMixin(PermissionRequiredForAppCrudMixin,
FormMixin,
DetailView):
model = SessaoPlenaria
app_label = AppConfig.label,
class PresencaMixin: class PresencaMixin:
def get_presencas(self): def get_presencas(self):
@ -455,14 +471,13 @@ class PresencaView(FormMixin, PresencaMixin, DetailView):
form_class = PresencaForm form_class = PresencaForm
model = SessaoPlenaria model = SessaoPlenaria
@method_decorator(permission_required(( def get_context_data(self, **kwargs):
'%s.add_%s' % ( context = FormMixin.get_context_data(self, **kwargs)
AppConfig.label, SessaoPlenariaPresenca._meta.model_name), context['title'] = '%s <small>(%s)</small>' % (
'%s.change_%s' % ( _('Presença'), self.object)
AppConfig.label, SessaoPlenariaPresenca._meta.model_name), return context
'%s.delete_%s' % (
AppConfig.label, SessaoPlenariaPresenca._meta.model_name), @method_decorator(permission_required_for_app(AppConfig.label))
)))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = self.get_form() form = self.get_form()
@ -502,9 +517,33 @@ class PresencaView(FormMixin, PresencaMixin, DetailView):
return reverse('sapl.sessao:presenca', kwargs={'pk': pk}) return reverse('sapl.sessao:presenca', kwargs={'pk': pk})
class PainelView(PermissionRequiredMixin, TemplateView): class PainelView(PermissionRequiredForAppCrudMixin, TemplateView):
template_name = 'sessao/painel.html' template_name = 'sessao/painel.html'
permission_required = permissoes_painel() app_label = 'painel'
def has_permission(self):
painel_aberto = AppsAppConfig.attr('painel_aberto')
if painel_aberto and self.request.user.is_anonymous():
return True
return PermissionRequiredForAppCrudMixin.has_permission(self)
def get(self, request, *args, **kwargs):
if request.user.is_anonymous():
self.template_name = 'painel/index.html'
return TemplateView.get(self, request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = TemplateView.get_context_data(self, **kwargs)
context.update({
'head_title': str(_('Painel Plenário')),
'sessao_id': kwargs['pk'],
'root_pk': kwargs['pk'],
'sessaoplenaria': SessaoPlenaria.objects.get(pk=kwargs['pk'])})
return context
class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView): class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView):
@ -512,11 +551,13 @@ class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView):
form_class = PresencaForm form_class = PresencaForm
model = SessaoPlenaria model = SessaoPlenaria
@method_decorator(permission_required(( def get_context_data(self, **kwargs):
'%s.add_%s' % (AppConfig.label, PresencaOrdemDia._meta.model_name), context = FormMixin.get_context_data(self, **kwargs)
'%s.change_%s' % (AppConfig.label, PresencaOrdemDia._meta.model_name), context['title'] = '%s <small>(%s)</small>' % (
'%s.delete_%s' % (AppConfig.label, PresencaOrdemDia._meta.model_name), _('Presença Ordem do Dia'), self.object)
))) return context
@method_decorator(permission_required_for_app(AppConfig.label))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -524,9 +565,6 @@ class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView):
pk = kwargs['pk'] pk = kwargs['pk']
if not self.request.user.has_perms(permissoes_sessao()):
return self.form_invalid(form)
if form.is_valid(): if form.is_valid():
# Pegar os presentes salvos no banco # Pegar os presentes salvos no banco
presentes_banco = PresencaOrdemDia.objects.filter( presentes_banco = PresencaOrdemDia.objects.filter(
@ -599,11 +637,7 @@ class ListMateriaOrdemDiaView(FormMixin, DetailView):
return self.render_to_response(context) return self.render_to_response(context)
@method_decorator(permission_required(( @method_decorator(permission_required_for_app(AppConfig.label))
'%s.add_%s' % (AppConfig.label, OrdemDia._meta.model_name),
'%s.change_%s' % (AppConfig.label, OrdemDia._meta.model_name),
'%s.delete_%s' % (AppConfig.label, OrdemDia._meta.model_name),
)))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
context = self.get_context_data(object=self.object) context = self.get_context_data(object=self.object)
@ -694,11 +728,13 @@ class MesaView(FormMixin, DetailView):
return self.render_to_response(context) return self.render_to_response(context)
@method_decorator(permission_required(( def get_context_data(self, **kwargs):
'%s.add_integrantemesa' % AppConfig.label, context = FormMixin.get_context_data(self, **kwargs)
'%s.change_integrantemesa' % AppConfig.label, context['title'] = '%s <small>(%s)</small>' % (
'%s.delete_integrantemesa' % AppConfig.label, _('Mesa Diretora'), self.object)
))) return context
@method_decorator(permission_required_for_app(AppConfig.label))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = MesaForm(request.POST) form = MesaForm(request.POST)
@ -761,7 +797,6 @@ class MesaView(FormMixin, DetailView):
lista_cargos_ocupados.append(cargo) lista_cargos_ocupados.append(cargo)
lista = list(set(lista_cargos) - set(lista_cargos_ocupados)) lista = list(set(lista_cargos) - set(lista_cargos_ocupados))
lista.sort(key=lambda x: x.descricao)
return lista return lista
def get_success_url(self): def get_success_url(self):
@ -769,8 +804,9 @@ class MesaView(FormMixin, DetailView):
return reverse('sapl.sessao:mesa', kwargs={'pk': pk}) return reverse('sapl.sessao:mesa', kwargs={'pk': pk})
class ResumoView(SessaoCrud.CrudDetailView): class ResumoView(DetailView):
template_name = 'sessao/resumo.html' template_name = 'sessao/resumo.html'
model = SessaoPlenaria
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -941,18 +977,22 @@ class ResumoView(SessaoCrud.CrudDetailView):
return self.render_to_response(context) return self.render_to_response(context)
class ExpedienteView(FormMixin, class ExpedienteView(FormMixin, DetailView):
SessaoCrud.CrudDetailView):
template_name = 'sessao/expediente.html' template_name = 'sessao/expediente.html'
form_class = ExpedienteForm form_class = ExpedienteForm
model = SessaoPlenaria
def get_context_data(self, **kwargs):
context = FormMixin.get_context_data(self, **kwargs)
context['title'] = '%s <small>(%s)</small>' % (
_('Expediente Diversos'), self.object)
return context
@method_decorator(permission_required_for_app(AppConfig.label))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = ExpedienteForm(request.POST) form = ExpedienteForm(request.POST)
if not self.request.user.has_perms(permissoes_sessao()):
return self.form_invalid(form)
if form.is_valid(): if form.is_valid():
list_tipo = request.POST.getlist('tipo') list_tipo = request.POST.getlist('tipo')
list_conteudo = request.POST.getlist('conteudo') list_conteudo = request.POST.getlist('conteudo')
@ -1009,16 +1049,13 @@ class ExpedienteView(FormMixin,
return reverse('sapl.sessao:expediente', kwargs={'pk': pk}) return reverse('sapl.sessao:expediente', kwargs={'pk': pk})
class VotacaoEditView(PermissionRequiredMixin, class VotacaoEditView(SessaoPermissionMixin):
FormMixin,
SessaoCrud.CrudDetailView):
''' '''
Votação Simbólica e Secreta Votação Simbólica e Secreta
''' '''
template_name = 'sessao/votacao/votacao_edit.html' template_name = 'sessao/votacao/votacao_edit.html'
permission_required = permissoes_sessao()
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
@ -1085,9 +1122,7 @@ class VotacaoEditView(PermissionRequiredMixin,
kwargs={'pk': pk}) kwargs={'pk': pk})
class VotacaoView(PermissionRequiredMixin, class VotacaoView(SessaoPermissionMixin):
FormMixin,
SessaoCrud.CrudDetailView):
''' '''
Votação Simbólica e Secreta Votação Simbólica e Secreta
@ -1095,7 +1130,6 @@ class VotacaoView(PermissionRequiredMixin,
template_name = 'sessao/votacao/votacao.html' template_name = 'sessao/votacao/votacao.html'
form_class = VotacaoForm form_class = VotacaoForm
permission_required = permissoes_sessao()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -1207,11 +1241,8 @@ class VotacaoView(PermissionRequiredMixin,
kwargs={'pk': pk}) kwargs={'pk': pk})
class VotacaoNominalView(PermissionRequiredMixin, class VotacaoNominalView(SessaoPermissionMixin):
FormMixin,
SessaoCrud.CrudDetailView):
template_name = 'sessao/votacao/nominal.html' template_name = 'sessao/votacao/nominal.html'
permission_required = permissoes_sessao()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
ordem_id = kwargs['mid'] ordem_id = kwargs['mid']
@ -1335,11 +1366,8 @@ class VotacaoNominalView(PermissionRequiredMixin,
kwargs={'pk': pk}) kwargs={'pk': pk})
class VotacaoNominalEditView(PermissionRequiredMixin, class VotacaoNominalEditView(SessaoPermissionMixin):
FormMixin,
SessaoCrud.CrudDetailView):
template_name = 'sessao/votacao/nominal_edit.html' template_name = 'sessao/votacao/nominal_edit.html'
permission_required = permissoes_sessao()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
context = {} context = {}
@ -1415,11 +1443,8 @@ class VotacaoNominalEditView(PermissionRequiredMixin,
kwargs={'pk': pk}) kwargs={'pk': pk})
class VotacaoNominalExpedienteView(PermissionRequiredMixin, class VotacaoNominalExpedienteView(SessaoPermissionMixin):
FormMixin,
SessaoCrud.CrudDetailView):
template_name = 'sessao/votacao/nominal.html' template_name = 'sessao/votacao/nominal.html'
permission_required = permissoes_sessao()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
expediente_id = kwargs['mid'] expediente_id = kwargs['mid']
@ -1541,11 +1566,8 @@ class VotacaoNominalExpedienteView(PermissionRequiredMixin,
kwargs={'pk': pk}) kwargs={'pk': pk})
class VotacaoNominalExpedienteEditView(PermissionRequiredMixin, class VotacaoNominalExpedienteEditView(SessaoPermissionMixin):
FormMixin,
SessaoCrud.CrudDetailView):
template_name = 'sessao/votacao/nominal_edit.html' template_name = 'sessao/votacao/nominal_edit.html'
permission_required = permissoes_sessao()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
context = {} context = {}
@ -1620,9 +1642,7 @@ class VotacaoNominalExpedienteEditView(PermissionRequiredMixin,
kwargs={'pk': pk}) kwargs={'pk': pk})
class VotacaoExpedienteView(PermissionRequiredMixin, class VotacaoExpedienteView(SessaoPermissionMixin):
FormMixin,
SessaoCrud.CrudDetailView):
''' '''
Votação Simbólica e Secreta Votação Simbólica e Secreta
@ -1630,7 +1650,6 @@ class VotacaoExpedienteView(PermissionRequiredMixin,
template_name = 'sessao/votacao/votacao.html' template_name = 'sessao/votacao/votacao.html'
form_class = VotacaoForm form_class = VotacaoForm
permission_required = permissoes_sessao()
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -1744,9 +1763,7 @@ class VotacaoExpedienteView(PermissionRequiredMixin,
kwargs={'pk': pk}) kwargs={'pk': pk})
class VotacaoExpedienteEditView(PermissionRequiredMixin, class VotacaoExpedienteEditView(SessaoPermissionMixin):
FormMixin,
SessaoCrud.CrudDetailView):
''' '''
Votação Simbólica e Secreta Votação Simbólica e Secreta
@ -1754,7 +1771,6 @@ class VotacaoExpedienteEditView(PermissionRequiredMixin,
template_name = 'sessao/votacao/votacao_edit.html' template_name = 'sessao/votacao/votacao_edit.html'
form_class = VotacaoEditForm form_class = VotacaoEditForm
permission_required = permissoes_sessao()
def get_success_url(self): def get_success_url(self):
pk = self.kwargs['pk'] pk = self.kwargs['pk']
@ -1845,8 +1861,9 @@ class PautaSessaoListView(SessaoListView):
template_name = "sessao/pauta_sessao_list.html" template_name = "sessao/pauta_sessao_list.html"
class PautaSessaoDetailView(SessaoCrud.CrudDetailView): class PautaSessaoDetailView(DetailView):
template_name = "sessao/pauta_sessao_detail.html" template_name = "sessao/pauta_sessao_detail.html"
model = SessaoPlenaria
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -1956,8 +1973,9 @@ class SessaoPlenariaView(generics.ListAPIView):
serializer_class = SessaoPlenariaSerializer serializer_class = SessaoPlenariaSerializer
class PautaExpedienteDetail(SessaoCrud.CrudDetailView): class PautaExpedienteDetail(DetailView):
template_name = "sessao/pauta/expediente.html" template_name = "sessao/pauta/expediente.html"
model = SessaoPlenaria
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
pk = self.kwargs['pk'] pk = self.kwargs['pk']
@ -1974,8 +1992,9 @@ class PautaExpedienteDetail(SessaoCrud.CrudDetailView):
'tramitacao': tramitacao}) 'tramitacao': tramitacao})
class PautaOrdemDetail(SessaoCrud.CrudDetailView): class PautaOrdemDetail(DetailView):
template_name = "sessao/pauta/ordem.html" template_name = "sessao/pauta/ordem.html"
model = SessaoPlenaria
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
pk = self.kwargs['pk'] pk = self.kwargs['pk']
@ -2073,11 +2092,11 @@ def retira_materias_ja_adicionadas(id_sessao, model):
return lista_id_materias return lista_id_materias
class AdicionarVariasMateriasExpediente(PermissionRequiredMixin, class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin,
MateriaLegislativaPesquisaView): MateriaLegislativaPesquisaView):
filterset_class = AdicionarVariasMateriasFilterSet filterset_class = AdicionarVariasMateriasFilterSet
template_name = 'sessao/adicionar_varias_materias_expediente.html' template_name = 'sessao/adicionar_varias_materias_expediente.html'
permission_required = permissoes_sessao() app_label = AppConfig.label
def get_filterset_kwargs(self, filterset_class): def get_filterset_kwargs(self, filterset_class):
super(AdicionarVariasMateriasExpediente, super(AdicionarVariasMateriasExpediente,
@ -2138,6 +2157,8 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredMixin,
expediente = ExpedienteMateria() expediente = ExpedienteMateria()
expediente.sessao_plenaria_id = self.kwargs['pk'] expediente.sessao_plenaria_id = self.kwargs['pk']
expediente.materia_id = materia.id expediente.materia_id = materia.id
expediente.observacao = MateriaLegislativa.objects.get(
pk=materia.id).ementa
if lista_materias_expediente: if lista_materias_expediente:
posicao = lista_materias_expediente.last().numero_ordem + 1 posicao = lista_materias_expediente.last().numero_ordem + 1
expediente.numero_ordem = posicao expediente.numero_ordem = posicao
@ -2147,13 +2168,15 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredMixin,
expediente.tipo_votacao = request.POST['tipo_votacao_%s' % m] expediente.tipo_votacao = request.POST['tipo_votacao_%s' % m]
expediente.save() expediente.save()
return self.get(request, self.kwargs) pk = self.kwargs['pk']
return HttpResponseRedirect(
reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': pk}))
class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente): class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente):
filterset_class = AdicionarVariasMateriasFilterSet filterset_class = AdicionarVariasMateriasFilterSet
template_name = 'sessao/adicionar_varias_materias_ordem.html' template_name = 'sessao/adicionar_varias_materias_ordem.html'
permission_required = permissoes_sessao()
def get_filterset_kwargs(self, filterset_class): def get_filterset_kwargs(self, filterset_class):
super(AdicionarVariasMateriasExpediente, super(AdicionarVariasMateriasExpediente,
@ -2199,6 +2222,8 @@ class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente):
ordem_dia = OrdemDia() ordem_dia = OrdemDia()
ordem_dia.sessao_plenaria_id = self.kwargs['pk'] ordem_dia.sessao_plenaria_id = self.kwargs['pk']
ordem_dia.materia_id = materia.id ordem_dia.materia_id = materia.id
ordem_dia.observacao = MateriaLegislativa.objects.get(
pk=materia.id).ementa
if lista_materias_ordem_dia: if lista_materias_ordem_dia:
posicao = lista_materias_ordem_dia.last().numero_ordem + 1 posicao = lista_materias_ordem_dia.last().numero_ordem + 1
ordem_dia.numero_ordem = posicao ordem_dia.numero_ordem = posicao

10
sapl/templates/comissoes/materias_em_tramitacao.html

@ -3,12 +3,16 @@
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block actions %} {% endblock %} {% block actions %} {% endblock %}
{% block title %}
<h1 class="page-header">
Matérias em Tramitação <small>({{object}})</small>
</h1>
{% endblock %}
{% block detail_content %} {% block detail_content %}
<fieldset> <fieldset>
<legend>{{comissao}}</legend> <legend>{{comissao}}</legend>
<b>Há {{page_obj|length}} matéria(s) em tramitação nesta unidade.</b> <br><br> <b>Há {{page_obj|length}} matéria(s) em tramitação nesta unidade.</b> <br><br>
{% for materia in page_obj %} {% for materia in page_obj %}
<b><a href="{% url 'sapl.materia:materialegislativa_detail' materia.id %}"> <b><a href="{% url 'sapl.materia:materialegislativa_detail' materia.id %}">
{{materia.tipo.sigla}} {{materia.numero}} {{materia.ano}} - {{materia.tipo}} {{materia.tipo.sigla}} {{materia.numero}} {{materia.ano}} - {{materia.tipo}}
@ -18,7 +22,5 @@
<b>Situação: </b>{{materia.tramitacao_set.last.status.descricao}}<br> <b>Situação: </b>{{materia.tramitacao_set.last.status.descricao}}<br>
<br> <br>
{% endfor %} {% endfor %}
</fieldset> </fieldset>
{% endblock detail_content %} {% endblock detail_content %}

58
sapl/templates/crud/detail.html

@ -48,36 +48,38 @@
{% endfor %} {% endfor %}
{% endblock detail_content %} {% endblock detail_content %}
<div class="container-table"> {% block table_content %}
{% if not rows %} <div class="container-table">
<p>{{ NO_ENTRIES_MSG }}</p> {% if not rows %}
{% else %} <p>{{ NO_ENTRIES_MSG }}</p>
<div class="result-count">{% blocktrans with verbose_name_plural=view.model_set_verbose_name_plural %}Total de {{ verbose_name_plural }}: <strong>{{count}}</strong>{% endblocktrans %}</div> {% else %}
<table class="table table-striped table-hover"> <div class="result-count">{% blocktrans with verbose_name_plural=view.model_set_verbose_name_plural %}Total de {{ verbose_name_plural }}: <strong>{{count}}</strong>{% endblocktrans %}</div>
<thead> <table class="table table-striped table-hover">
<thead>
<tr>
{% for name in headers %}
<th>{{ name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for value_list in rows %}
<tr> <tr>
{% for name in headers %} {% for value, href in value_list %}
<th>{{ name }}</th> <td>
{% if href %}
<a href="{{ href }}">{{ value }}</a>
{% elif value != 'core.Cep.None' %}
{{ value|safe }}
{% endif %}
</td>
{% endfor %} {% endfor %}
</tr> </tr>
</thead>
<tbody>
{% for value_list in rows %}
<tr>
{% for value, href in value_list %}
<td>
{% if href %}
<a href="{{ href }}">{{ value }}</a>
{% elif value != 'core.Cep.None' %}
{{ value|safe }}
{% endif %}
</td>
{% endfor %} {% endfor %}
</tr> </tbody>
{% endfor %} </table>
</tbody> {% endif %}
</table> </div>
{% endif %} {% include "paginacao.html" %}
</div> {% endblock table_content %}
{% include "paginacao.html" %}
{% endblock base_content %} {% endblock base_content %}

104
sapl/templates/materia/materialegislativa_filter.html

@ -2,72 +2,62 @@
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block actions %} {% block actions %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
{% if user.is_authenticated %} {% if perms.materia %}
<a href="{% url 'sapl.materia:materialegislativa_create' %}" class="btn btn-default"> <a href="{% url 'sapl.materia:materialegislativa_create' %}" class="btn btn-default">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar Matéria Legislativa {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Adicionar Matéria Legislativa {% endblocktrans %}
</a> </a>
{% endif %}
{% if filter_url %}
<a href="{% url 'sapl.materia:pesquisar_materia' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a>
{% endif %} {% endif %}
{% if filter_url %} </div>
<a href="{% url 'sapl.materia:pesquisar_materia' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a> {% endblock %}
{% endif %}
</div>
{% endblock %}
{% block sections_nav %} {% block detail_content %}
{% endblock %} {% if not filter_url %}
{% crispy filter.form %}
{% block detail_content %} {% endif %}
{% if not filter_url %}
{% crispy filter.form %}
{% endif %}
<p></p> {% if filter_url %}
{% if filter_url %} <table class="table table-striped table-bordered">
<table class="table table-striped table-bordered">
<thead class="thead-default"> <thead class="thead-default">
<tr><td><h3>{% trans "Resultados" %}</h3></td></tr> <tr><td><h3>{% trans "Resultados" %}</h3></td></tr>
</thead> </thead>
{% if paginator.count %} {% if paginator.count %}
{% if paginator.count > 1 %} {% if paginator.count > 1 %}
<h3>{% blocktrans with paginator.count as total_materias %}Pesquisa concluída com sucesso! Foram encontradas {{total_materias}} matérias.{% endblocktrans %}</h3> <h3>{% blocktrans with paginator.count as total_materias %}Pesquisa concluída com sucesso! Foram encontradas {{total_materias}} matérias.{% endblocktrans %}</h3>
{% elif paginator.count == 1 %} {% elif paginator.count == 1 %}
<h3>{% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}</h3> <h3>{% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}</h3>
{% endif %} {% endif %}
{% for m in page_obj %} {% for m in page_obj %}
<tr> <tr>
<td> <td>
<strong><a href="{% url 'materia:materialegislativa_detail' m.id %}">{{m.tipo.sigla}} {{m.numero}}/{{m.ano}} - {{m.tipo}}</strong></a></br> <strong><a href="{% url 'materia:materialegislativa_detail' m.id %}">{{m.tipo.sigla}} {{m.numero}}/{{m.ano}} - {{m.tipo}}</strong></a></br>
<strong>Autores:</strong> <strong>Autores:</strong>
{% for a in m.autoria_set.all %} {% for a in m.autoria_set.all %}
{% if not forloop.first %} {% if not forloop.first %}
, &nbsp;&nbsp; {{a.autor|default_if_none:"Não Informado"}} , &nbsp;&nbsp; {{a.autor|default_if_none:"Não Informado"}}
{% else %} {% else %}
&nbsp;{{a.autor|default_if_none:"Não Informado"}} &nbsp;{{a.autor|default_if_none:"Não Informado"}}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</br> </br>
<strong>Localização Atual:</strong> &nbsp;{{m.tramitacao_set.last.unidade_tramitacao_destino|default_if_none:"Não Informada"}}</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> <strong>Status:</strong> &nbsp;{{m.tramitacao_set.last.status|default_if_none:"Não Informada"}}</br>
<strong>Data da última Tramitação:</strong> &nbsp;{{m.tramitacao_set.last.data_tramitacao|default_if_none:"Não Informada"}}</br> <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> <strong>Ementa:</strong>&nbsp;{{ m.ementa|safe }}</br>
<p></p> <p></p>
</tr> </tr>
{% endfor %} {% endfor %}
{% else %} {% else %}
<tr> <tr><td><h3>Nenhuma matéria encontrada com essas especificações</h3></tr>
<td>
<h3>Nenhuma matéria encontrada com essas especificações</h3>
</tr>
{% endif %} {% endif %}
</table>
</table> {% include "paginacao.html" %}
{% endif %}
{% endblock detail_content %}
{% include "paginacao.html" %} {% block table_content %}
{% endif %} {% endblock table_content %}
{% endblock detail_content %}

6
sapl/templates/materia/subnav.yaml

@ -11,16 +11,12 @@
url: documentoacessorio_list url: documentoacessorio_list
- title: {% trans 'Legislação Citada' %} - title: {% trans 'Legislação Citada' %}
url: legislacaocitada_list url: legislacaocitada_list
app_name: 'materia'
- title: {% trans 'Numeração' %} - title: {% trans 'Numeração' %}
url: numeracao_list url: numeracao_list
- title: {% trans 'Tramitação' %} - title: {% trans 'Tramitação' %}
url: tramitacao_list url: tramitacao_list
- title: {% trans 'Relatoria' %} - title: {% trans 'Relatoria' %}
url: relatoria_list url: relatoria_list
# Opção adicionada para chamar o TextoArticulado da matéria.
# para integração foram necessárias apenas criar a url materia_ta em urls.py
# e a view MateriaTaView(IntegracaoTaView) em views.py
# Em nada mais a integração interfere em MateriaLegislativa
- title: {% trans 'Texto' %} - title: {% trans 'Texto' %}
url: materia_ta url: materia_ta

82
sapl/templates/menus/mesa_diretora/mesa_diretora.html

@ -1,82 +0,0 @@
{% extends "crud/detail.html" %}
{% load i18n %}
{% block actions %} {% endblock %}
{% block detail_content %}
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend>Escolha da Legislatura e da Sessão Legislativa</legend>
<div class="row">
<div class="col-md-6">
<label>Escolha uma Legislatura</label>
<select name="legislatura" class="form-control" onChange="form.submit();">
{% for l in legislaturas %}
<option value="{{l.id}}" {% if l == legislatura_selecionada %} selected {% endif %}>
{{l}}
</option>
{% endfor %}
</select>
</div>
<div class="col-md-6">
<label>Escolha uma Sessão Legislativa</label>
<select name="sessao" class="form-control" onChange="form.submit();">
{% for s in sessoes %}
<option value="{{s.id}}" {% if s == sessao_selecionada %} selected {% endif %}>
{{s}}
</option>
{% endfor %}
</select>
</div>
</div>
</fieldset>
<br />
<fieldset class="form-group">
<legend>Escolha da Composição da Mesa Diretora</legend>
<div class="row">
<div class="col-md-4">
<label>Composição da Mesa Diretora</label>
<select multiple size="5" class="form-control" name="composicao_mesa">
{% for p in composicao_mesa %}
<option value="{{p.parlamentar.id}}:{{p.cargo.id}}">
{{p.parlamentar}} || {{p.cargo}}
</option>
{% endfor %}
</select>
</div>
<div class="col-md-4" align="center">
<br /><br />
{% if cargos_vagos %}
{% if perms.parlamentares.add_cargomesa or perms.parlamentares.add_composicaomesa %}
<input type="submit" name="Incluir" Value="Incluir" class="btn btn-primary" />
{% endif %}
{% endif %}
<br />
<br />
{% if perms.parlamentares.add_cargomesa or perms.parlamentares.add_composicaomesa %}
<input type="submit" name="Excluir" Value="Excluir" class="btn btn-danger" />
{% endif %}
</div>
{% if cargos_vagos %}
<div class="col-md-4">
<label>Parlamentare | Cargo</label>
<select class="form-control" name="parlamentar" id="parlamentar">
{% for p in parlamentares %}
<option value="{{p.id}}">{{p.nome_completo}}</option>
{% endfor %}
</select>
<br />
<select class="form-control" name="cargo" id="cargo">
{% for c in cargos_vagos %}
<option value="{{c.id}}">{{c}}</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
</fieldset>
</form>
{% endblock detail_content %}

8
sapl/templates/menus/subnav.html

@ -12,12 +12,16 @@
</a> </a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
{% for subitem in item.children %} {% for subitem in item.children %}
<li class="{{subitem.active}}"><a href="{{ subitem.url }}">{% trans subitem.title %}</a></li> {% if subitem.url %}
<li class="{{subitem.active}}"><a href="{{ subitem.url }}">{% trans subitem.title %}</a></li>
{% endif %}
{% endfor %} {% endfor %}
</ul> </ul>
</li> </li>
{% else %} {% else %}
<li class="{{item.active}}"><a href="{{ item.url }}">{% trans item.title %}</a></li> {% if item.url %}
<li class="{{item.active}}"><a href="{{ item.url }}">{% trans item.title %}</a></li>
{% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</ul> </ul>

1
sapl/templates/parlamentares/subnav.yaml

@ -7,6 +7,7 @@
url: filiacao_list url: filiacao_list
- title: {% trans 'Dependentes' %} - title: {% trans 'Dependentes' %}
url: dependente_list url: dependente_list
check_permission: parlamentares.list_dependente
- title: {% trans 'Comissões' %} - title: {% trans 'Comissões' %}
url: participacao_parlamentar_list url: participacao_parlamentar_list
- title: {% trans 'Proposições' %} - title: {% trans 'Proposições' %}

73
sapl/templates/protocoloadm/documentoadministrativo_filter.html

@ -14,54 +14,35 @@
</div> </div>
{% if filter_url %} {% if filter_url %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
<a href="{% url 'protocoloadm:pesq_doc_adm' %}" class="btn btn-default">{% trans 'Fazer Nova Pesquisa' %}</a> <a href="{% url 'protocoloadm:pesq_doc_adm' %}" class="btn btn-default">{% trans 'Fazer Nova Pesquisa' %}</a>
</div> </div>
{% endif %} {% endif %}
{% endblock actions %} {% endblock actions %}
{% block base_content %}
{% if not filter_url %} {% crispy filter.form %} {% endif %}
{% block detail_content %} {% if filter_url %}
<table class="table table-striped table-bordered">
{% if not filter_url %} <thead class="thead-default"><tr><td><h3>Resultados</h3></td></tr></thead>
{% if page_obj|length %}
{% crispy filter.form %} {% if numero_res > 1 %}
<h3>Pesquisa concluída com sucesso! Foram encontrados {{numero_res}} documentos.</h3>
{% endif %} {% elif numero_res == 1 %}
<h3>Pesquisa concluída com sucesso! Foi encontrado {{numero_res}} documento.</h3>
{% if filter_url %}
<p></p>
<table class="table table-striped table-bordered">
<thead class="thead-default">
<tr><td><h3>Resultados</h3></td></tr>
</thead>
{% if page_obj|length %}
{% if numero_res > 1 %}
<h3>Pesquisa concluída com sucesso! Foram encontrados {{numero_res}} documentos.</h3>
{% elif numero_res == 1 %}
<h3>Pesquisa concluída com sucesso! Foi encontrado {{numero_res}} documento.</h3>
{% endif %}
{% for d in page_obj %}
<tr>
<td>
<strong><a href="{% url 'protocoloadm:documentoadministrativo_detail' d.id %}">{{d.tipo.sigla}} {{d.numero}}/{{d.ano}} - {{d.tipo}}</strong></a></br>
<strong>Interessado:</strong>&nbsp;{{ d.interessado|default_if_none:"Não Informado"}}</br>
<strong>Assunto:</strong>&nbsp;{{ d.assunto|safe }}</br>
<p></p>
</tr>
{% endfor %}
{% else %}
<tr>
<td>
<h3>Nenhum documento encontrado com essas especificações</h3>
</tr>
{% endif %} {% endif %}
</table>
{% include "paginacao.html" %} {% for d in page_obj %}
{% endif %} <tr>
<td>
{% endblock detail_content %} <strong><a href="{% url 'protocoloadm:documentoadministrativo_detail' d.id %}">{{d.tipo.sigla}} {{d.numero}}/{{d.ano}} - {{d.tipo}}</strong></a></br>
<strong>Interessado:</strong>&nbsp;{{ d.interessado|default_if_none:"Não Informado"}}</br>
<strong>Assunto:</strong>&nbsp;{{ d.assunto|safe }}</br>
</td>
</tr>
{% endfor %}
{% else %}
<tr><td><h3>Nenhum documento encontrado com essas especificações</h3></tr>
{% endif %}
</table>
{% include "paginacao.html" %}
{% endif %}
{% endblock base_content %}

95
sapl/templates/protocoloadm/protocolo_filter.html

@ -5,76 +5,49 @@
{% block sections_nav %} {% endblock %} {% block sections_nav %} {% endblock %}
{% block detail_content %} {% block base_content %}
<h1><b>Pesquisa de Protocolo</b></h1> <h1><b>Pesquisa de Protocolo</b></h1>
<br></br> <br></br>
{% if filter_url %} {% if filter_url %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
<a href="{% url 'protocoloadm:protocolo' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a> <a href="{% url 'protocoloadm:protocolo' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a>
</div> </div>
{% endif %} {% endif %}
{% if not filter_url %} {% if not filter_url %}
{% crispy filter.form %} {% crispy filter.form %}
{% endif %} {% endif %}
{% if filter_url %} {% if filter_url %}
<p></p> <table class="table table-striped table-bordered">
<table class="table table-striped table-bordered"> <thead class="thead-default"><tr><td><h3>Resultados</h3></td></tr></thead>
<thead class="thead-default"> {% if page_obj|length %}
<tr><td><h3>Resultados</h3></td></tr> {% if filter_url %}
</thead> {% if numero_res > 1 %}
{% if page_obj|length %} <h3>Pesquisa concluída com sucesso! Foram encontrados {{numero_res}} protocolos.</h3>
{% if filter_url %} {% elif numero_res == 1 %}
{% if numero_res > 1 %} <h3>Pesquisa concluída com sucesso! Foi encontrado {{numero_res}} protocolo.</h3>
<h3>Pesquisa concluída com sucesso! Foram encontrados {{numero_res}} protocolos.</h3>
{% elif numero_res == 1 %}
<h3>Pesquisa concluída com sucesso! Foi encontrado {{numero_res}} protocolo.</h3>
{% endif %}
{% endif %} {% endif %}
{% for p in page_obj %}
<tr>
<td>
<strong>Protocolo:
<a href="{% url 'protocoloadm:protocolo_mostrar' p.numero p.ano %}">
{{ p.numero|stringformat:'06d' }}/{{ p.ano }}
</a></strong>
&nbsp;&nbsp;<strong>-</strong>&nbsp;&nbsp;
<a href="{% url 'relatorios:relatorio_etiqueta_protocolo' p.numero p.ano %}">
<img src="{% static 'img/etiqueta.png' %}" alt="Etiqueta Individual">
</a></br>
<strong>Assunto:</strong> {{ p.assunto_ementa|default_if_none:"Não Informado"}}</br>
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não Informado" }} - Horário: {{ p.hora|date:"G:i:s" }}</br>
<strong>Interessado:</strong> {{ p.interessado }}</br>
<strong>Natureza do Processo:</strong>
{% if p.tipo_processo == 0 %}
Administrativo
{% elif p.tipo_processo == 1 %}
Matéria Legislativa
{% endif %}</br>
<strong>Classificação:</strong> {{ p.tipo_documento|default_if_none:"Não Informado" }} </br>
<p></p>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td>
<h3>Nenhum protocolo encontrado com essas especificações</h3>
</tr>
{% endif %} {% endif %}
{% for p in page_obj %}
<tr>
<td>
<strong>Protocolo:
<a href="{% url 'protocoloadm:protocolo_mostrar' p.numero p.ano %}">{{ p.numero|stringformat:'06d' }}/{{ p.ano }}</a></strong>&nbsp;&nbsp;<strong>-</strong>&nbsp;&nbsp;
<a href="{% url 'relatorios:relatorio_etiqueta_protocolo' p.numero p.ano %}"><img src="{% static 'img/etiqueta.png' %}" alt="Etiqueta Individual"></a></br>
<strong>Assunto:</strong> {{ p.assunto_ementa|default_if_none:"Não Informado"}}</br>
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não Informado" }} - Horário: {{ p.hora|date:"G:i:s" }}</br>
<strong>Interessado:</strong> {{ p.interessado }}</br>
<strong>Natureza do Processo:</strong>
{% if p.tipo_processo == 0 %} Administrativo {% elif p.tipo_processo == 1 %} Matéria Legislativa {% endif %}</br>
<strong>Classificação:</strong> {{ p.tipo_documento|default_if_none:"Não Informado" }} </br>
</td>
</tr>
{% endfor %}
{% else %}
<tr><td><h3>Nenhum protocolo encontrado com essas especificações</h3></tr>
{% endif %}
</table> </table>
{% include "paginacao.html" %}
{% include "paginacao.html" %} {% endif %}
{% endblock base_content %}
{% endif %}
{% endblock detail_content %}

1
sapl/templates/sessao/expedientemateria_form.html

@ -6,6 +6,7 @@
{% block extra_js %} {% block extra_js %}
<script language="Javascript"> <script language="Javascript">
document.getElementById("id_observacao").readOnly = true;
function recuperar_materia() { function recuperar_materia() {
var tipo_materia = $("#id_tipo_materia").val() var tipo_materia = $("#id_tipo_materia").val()

6
sapl/templates/sessao/layouts.yaml

@ -28,11 +28,13 @@ RegistroVotacao:
Orador: Orador:
{% trans 'Orador das Explicações Pessoais' %}: {% trans 'Orador das Explicações Pessoais' %}:
- numero_ordem parlamentar url_discurso - numero_ordem parlamentar
- url_discurso observacao
OradorExpediente: OradorExpediente:
{% trans 'Orador do Expediente' %}: {% trans 'Orador do Expediente' %}:
- numero_ordem parlamentar url_discurso - numero_ordem parlamentar
- url_discurso observacao
ExpedienteMateria: ExpedienteMateria:
{% trans 'Matéria do Expediente' %}: {% trans 'Matéria do Expediente' %}:

8
sapl/templates/sessao/painel.html

@ -2,6 +2,14 @@
{% load i18n %} {% load i18n %}
{% load staticfiles %} {% load staticfiles %}
{% block actions %} {% endblock %} {% block actions %} {% endblock %}
{% block title %}
<h1 class="page-header">
Painel Eletrônico <small>({{sessaoplenaria}})</small>
</h1>
{% endblock %}
{% block detail_content %} {% block detail_content %}
<audio type="hidden" id="audio" src="{% static 'audio/ring.mp3' %}"> </audio> <audio type="hidden" id="audio" src="{% static 'audio/ring.mp3' %}"> </audio>

63
sapl/templates/sessao/pauta_sessao_filter.html

@ -5,45 +5,36 @@
{% block sections_nav %} {% endblock %} {% block sections_nav %} {% endblock %}
{% block detail_content %} {% block base_content %}
<h1><b>Pesquisar Pauta de Sessão</b></h1> <h1><b>Pesquisar Pauta de Sessão</b></h1>
<br></br> <br></br>
{% crispy filter.form %} {% if not filter_url %}
<p></p> {% crispy filter.form %}
<table class="table table-striped table-hover"> {% endif %}
<thead class="thead-default">
<tr><td><h3>Resultados</h3></td></tr> {% if filter_url %}
</thead> <p></p>
{% if page_obj|length %} <table class="table table-striped table-hover">
{% if numero_res > 1 %} <thead class="thead-default"><tr><td><h3>Resultados</h3></td></tr></thead>
<h3>Foram encontradas {{numero_res}} pautas de sessões.</h3></br> {% if page_obj|length %}
{% elif numero_res == 1 %} {% if numero_res > 1 %}
<h3>Foi encontrada {{numero_res}} pauta de sessão.</h3></br> <h3>Foram encontradas {{numero_res}} pautas de sessões.</h3></br>
{% endif %} {% elif numero_res == 1 %}
<h3>Foi encontrada {{numero_res}} pauta de sessão.</h3></br>
{% for s in page_obj %}
<tr>
<td>
<a href="{% url 'sapl.sessao:pauta_sessao_detail' s.id %}"><strong>{{s}}</strong></br></a>
</td>
<td>
<a href="{% url 'sapl.relatorios:relatorio_sessao_plenaria' s.id %}">
<img src="{% static 'img/file.png' %}">
</a>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td>
<h3>Nenhuma Pauta de Sessão encontrada com essas especificações</h3>
</tr>
{% endif %} {% endif %}
{% for s in page_obj %}
<tr>
<td><a href="{% url 'sapl.sessao:pauta_sessao_detail' s.id %}"><strong>{{s}}</strong></br></a></td><td>
<a href="{% url 'sapl.relatorios:relatorio_sessao_plenaria' s.id %}"><img src="{% static 'img/file.png' %}"></a>
</td>
</tr>
{% endfor %}
{% else %}
<tr><td><h3>Nenhuma Pauta de Sessão encontrada com essas especificações</h3></tr>
{% endif %}
</table> </table>
{% include "paginacao.html" %}
{% include "paginacao.html" %} {% endif %}
{% endblock base_content %}
{% endblock detail_content %}

6
sapl/templates/sessao/resumo.html

@ -2,6 +2,12 @@
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block title %}
<h1 class="page-header">
Resumo <small>({{sessaoplenaria}})</small>
</h1>
{% endblock %}
{% block detail_content %} {% block detail_content %}
<fieldset> <fieldset>
<legend>Identificação Básica</legend> <legend>Identificação Básica</legend>

13
sapl/templates/sessao/sessaoplenaria_filter.html

@ -18,17 +18,18 @@
{% block sections_nav %} {% endblock %} {% block sections_nav %} {% endblock %}
{% block detail_content %} {% block base_content %}
<h1><b>Pesquisar Sessão Plenária</b></h1> <h1><b>Pesquisar Sessão Plenária</b></h1>
<br></br> <br></br>
{% crispy filter.form %} {% if not filter_url %}
{% crispy filter.form %}
{% endif %}
{% if filter_url %} {% if filter_url %}
<p></p> <p></p>
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-default"> <thead class="thead-default"><tr><td><h3>Resultados</h3></td></tr></thead>
<tr><td><h3>Resultados</h3></td></tr>
</thead>
{% if page_obj|length %} {% if page_obj|length %}
{% if numero_res > 1 %} {% if numero_res > 1 %}
<h3>Foram encontradas {{numero_res}} sessões.</h3></br> <h3>Foram encontradas {{numero_res}} sessões.</h3></br>
@ -53,4 +54,4 @@
</table> </table>
{% include "paginacao.html" %} {% include "paginacao.html" %}
{% endif %} {% endif %}
{% endblock detail_content %} {% endblock base_content %}

5
sapl/templates/sessao/subnav.yaml

@ -1,4 +1,5 @@
{% load i18n %} {% load i18n common_tags %}
- title: {% trans 'Abertura' %} - title: {% trans 'Abertura' %}
children: children:
- title: {% trans 'Dados Básicos' %} - title: {% trans 'Dados Básicos' %}
@ -28,6 +29,6 @@
- title: {% trans 'Painel Eletrônico' %} - title: {% trans 'Painel Eletrônico' %}
url: painel url: painel
{% if not 'painel_aberto'|get_config_attr %}check_permission: painel.list_painel{%endif%}
- title: {% trans 'Resumo' %} - title: {% trans 'Resumo' %}
url: resumo url: resumo

22
sapl/utils.py

@ -8,9 +8,10 @@ from django import forms
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import PermissionDenied, ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from floppyforms import ClearableFileInput from floppyforms import ClearableFileInput
@ -279,6 +280,25 @@ def permissoes(nome_grupo, app_label):
return set(lista_permissoes) return set(lista_permissoes)
def permission_required_for_app(app_label, login_url=None,
raise_exception=False):
"""
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.
If the raise_exception parameter is given the PermissionDenied exception
is raised.
"""
def check_perms(user):
if user.has_module_perms(app_label):
return True
# In case the 403 handler should be called raise the exception
if raise_exception:
raise PermissionDenied
# As the last resort, show the login form
return False
return user_passes_test(check_perms, login_url=login_url)
def permissoes_materia(): def permissoes_materia():
return permissoes('Operador de Matéria', 'materia') return permissoes('Operador de Matéria', 'materia')

2
scripts/inicializa_grupos_autorizacoes.py

@ -1,6 +1,6 @@
import os import os
import django
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sapl.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sapl.settings")
django.setup() django.setup()

74
scripts/test_inicializa_grupos_autorizacoes.py

@ -1,29 +1,94 @@
import pytest
from django.apps import apps from django.apps import apps
from django.contrib.auth.management import _get_all_permissions
from django.contrib.auth.models import Group, Permission from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
import pytest from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from inicializa_grupos_autorizacoes import cria_grupos_permissoes from inicializa_grupos_autorizacoes import cria_grupos_permissoes
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
apps_com_permissao_padrao = [ apps_com_permissao_padrao = [
'comissoes', 'norma', 'sessao', 'painel'] 'comissoes', 'norma', 'sessao', 'painel']
def create_perms_post_migrate(app):
searched_perms = list()
# The codenames and ctypes that should exist.
ctypes = set()
for klass in list(app.get_models()):
opts = klass._meta
permissions = (
("list_" + opts.model_name,
string_concat(
_('Visualizaçao da lista de'), ' ',
opts.verbose_name_plural)),
("detail_" + opts.model_name,
string_concat(
_('Visualização dos detalhes de'), ' ',
opts.verbose_name_plural)),
)
opts.permissions = tuple(
set(list(permissions) + list(opts.permissions)))
if opts.proxy:
# Force looking up the content types in the current database
# before creating foreign keys to them.
app_label, model = opts.app_label, opts.model_name
try:
ctype = ContentType.objects.get_by_natural_key(
app_label, model)
except:
ctype = ContentType.objects.create(
app_label=app_label, model=model)
else:
ctype = ContentType.objects.get_for_model(klass)
ctypes.add(ctype)
for perm in _get_all_permissions(klass._meta, ctype):
searched_perms.append((ctype, perm))
all_perms = set(Permission.objects.filter(
content_type__in=ctypes,
).values_list(
"content_type", "codename"
))
perms = [
Permission(codename=codename, name=name, content_type=ct)
for ct, (codename, name) in searched_perms
if (ct.pk, codename) not in all_perms
]
Permission.objects.bulk_create(perms)
@pytest.mark.parametrize('app_label', apps_com_permissao_padrao) @pytest.mark.parametrize('app_label', apps_com_permissao_padrao)
def test_grupo_padrao_tem_permissoes_sobre_todo_o_app(app_label): def test_grupo_padrao_tem_permissoes_sobre_todo_o_app(app_label):
app = apps.get_app_config(app_label) app = apps.get_app_config(app_label)
create_perms_post_migrate(app)
# código testado # código testado
cria_grupos_permissoes() cria_grupos_permissoes()
def gerar_permissoes(app): def gerar_permissoes(app):
for model in app.get_models(): for model in app.get_models():
for op in ['add', 'change', 'delete']: for op in ['add', 'change', 'delete', ]:
yield model, 'Can %s %s' % (op, model._meta.verbose_name) yield model, 'Can %s %s' % (op, model._meta.verbose_name)
yield model, force_text(string_concat(
_('Visualizaçao da lista de'), ' ',
model._meta.verbose_name_plural))
yield model, force_text(string_concat(
_('Visualização dos detalhes de'),
' ',
model._meta.verbose_name_plural))
grupo = Group.objects.get(name='Operador de %s' % app.verbose_name) grupo = Group.objects.get(name='Operador de %s' % app.verbose_name)
esperado = set(gerar_permissoes(app)) esperado = set(gerar_permissoes(app))
@ -36,6 +101,9 @@ def test_grupo_padrao_tem_permissoes_sobre_todo_o_app(app_label):
def test_permissoes_extras_sao_apagadas(app_label): def test_permissoes_extras_sao_apagadas(app_label):
app = apps.get_app_config(app_label) app = apps.get_app_config(app_label)
# create_perms_post_migrate(app)
grupo = Group.objects.create(name='Operador de %s' % app.verbose_name) grupo = Group.objects.create(name='Operador de %s' % app.verbose_name)
permissao_errada = Permission.objects.create( permissao_errada = Permission.objects.create(

Loading…
Cancel
Save