Browse Source

Adicionar Pauta Reunião (#2778)

* Refatorar reuniao_detail p/ add seleção de pauta

* Refatorar reuniao_detail p/ add CRUD Pauta Reunião

* Adicionar model PautaReuniao e migração

* Adicionar config. iniciais p/ CRUD Pauta

* Refatorar código

* Adicionar CRUD Pauta Reunião

* Adicionar permissões necessárias

* Update sapl/comissoes/views.py

Co-Authored-By: Edward <edwardoliveira@users.noreply.github.com>

* Update sapl/comissoes/views.py

Co-Authored-By: Edward <edwardoliveira@users.noreply.github.com>

* Update sapl/comissoes/views.py

Co-Authored-By: Edward <edwardoliveira@users.noreply.github.com>

* Atualizar sapl/comissoes/views.py

* Update views.py
pull/2811/head
João Rodrigues 6 years ago
committed by Cesar Carvalho
parent
commit
fb44cadf31
  1. 8
      sapl/comissoes/forms.py
  2. 6
      sapl/comissoes/urls.py
  3. 161
      sapl/comissoes/views.py
  4. 29
      sapl/materia/migrations/0049_pautareuniao.py
  5. 26
      sapl/materia/models.py
  6. 4
      sapl/materia/views.py
  7. 2
      sapl/protocoloadm/views.py
  8. 1
      sapl/rules/map_rules.py
  9. 68
      sapl/templates/comissoes/pauta.html
  10. 65
      sapl/templates/comissoes/reuniao_detail.html

8
sapl/comissoes/forms.py

@ -11,6 +11,7 @@ from django.utils.translation import ugettext_lazy as _
from sapl.base.models import Autor, TipoAutor
from sapl.comissoes.models import (Comissao, Composicao, DocumentoAcessorio,
Participacao, Reuniao, Periodo)
from sapl.materia.models import PautaReuniao
from sapl.parlamentares.models import Legislatura, Mandato, Parlamentar
from sapl.utils import FileFieldCheckMixin
@ -383,6 +384,13 @@ class ReuniaoForm(ModelForm):
return self.cleaned_data
class PautaReuniaoForm(forms.ModelForm):
class Meta:
model = PautaReuniao
exclude = ['reuniao']
class DocumentoAcessorioCreateForm(FileFieldCheckMixin, forms.ModelForm):
parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput())

6
sapl/comissoes/urls.py

@ -1,7 +1,8 @@
from django.conf.urls import include, url
from sapl.comissoes.views import (CargoCrud, ComissaoCrud, ComposicaoCrud,
DocumentoAcessorioCrud, MateriasTramitacaoListView, ParticipacaoCrud,
PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud, get_participacoes_comissao)
PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud, get_participacoes_comissao,
AdicionaPautaView, RemovePautaView)
from .apps import AppConfig
@ -17,6 +18,9 @@ urlpatterns = [
url(r'^comissao/(?P<pk>\d+)/materias-em-tramitacao$',
MateriasTramitacaoListView.as_view(), name='materias_em_tramitacao'),
url(r'^comissao/(?P<pk>\d+)/pauta/add', AdicionaPautaView.as_view(), name='pauta_add'),
url(r'^comissao/(?P<pk>\d+)/pauta/remove', RemovePautaView.as_view(), name='pauta_remove'),
url(r'^sistema/comissao/cargo/', include(CargoCrud.get_urls())),
url(r'^sistema/comissao/periodo-composicao/',
include(PeriodoComposicaoCrud.get_urls())),

161
sapl/comissoes/views.py

@ -1,13 +1,16 @@
import logging
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import F
from django.http.response import HttpResponseRedirect, JsonResponse
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import ListView
from django.views.generic import ListView, CreateView, DeleteView
from django.views.generic.base import RedirectView
from django.views.generic.detail import DetailView
from django.views.generic.edit import FormMixin
from django.views.generic.edit import FormMixin, UpdateView
from django.utils.translation import ugettext_lazy as _
from sapl.base.models import AppConfig as AppsAppConfig
from sapl.comissoes.apps import AppConfig
@ -15,11 +18,11 @@ from sapl.comissoes.forms import (ComissaoForm, ComposicaoForm,
DocumentoAcessorioCreateForm,
DocumentoAcessorioEditForm,
ParticipacaoCreateForm, ParticipacaoEditForm,
PeriodoForm, ReuniaoForm)
PeriodoForm, ReuniaoForm, PautaReuniaoForm)
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
MasterDetailCrud,
PermissionRequiredForAppCrudMixin)
from sapl.materia.models import MateriaLegislativa, Tramitacao
from sapl.materia.models import MateriaLegislativa, Tramitacao, PautaReuniao
from .models import (CargoComissao, Comissao, Composicao, DocumentoAcessorio,
Participacao, Periodo, Reuniao, TipoComissao)
@ -162,25 +165,26 @@ class ComissaoCrud(Crud):
return super(Crud.UpdateView, self).form_valid(form)
def lista_materias_comissao(comissao_pk):
ts = Tramitacao.objects.order_by(
'materia', '-data_tramitacao', '-id').annotate(
comissao=F('unidade_tramitacao_destino__comissao')).distinct(
'materia').values_list('materia', 'comissao')
ts = [m for (m,c) in ts if c == int(comissao_pk)]
materias = MateriaLegislativa.objects.filter(
pk__in=ts).order_by('tipo', '-ano', '-numero')
return materias
class MateriasTramitacaoListView(ListView):
template_name = "comissoes/materias_em_tramitacao.html"
paginate_by = 10
def get_queryset(self):
# FIXME: Otimizar consulta
ts = Tramitacao.objects.order_by(
'materia', '-data_tramitacao', '-id').annotate(
comissao=F('unidade_tramitacao_destino__comissao')).distinct(
'materia').values_list('materia', 'comissao')
ts = list(filter(lambda x: x[1] == int(self.kwargs['pk']), ts))
ts = list(zip(*ts))
ts = ts[0] if ts else []
materias = MateriaLegislativa.objects.filter(
pk__in=ts).order_by('tipo', '-ano', '-numero')
return materias
return lista_materias_comissao(self.kwargs['pk'])
def get_context_data(self, **kwargs):
context = super(
@ -193,13 +197,38 @@ class MateriasTramitacaoListView(ListView):
class ReuniaoCrud(MasterDetailCrud):
model = Reuniao
parent_field = 'comissao'
model_set = 'documentoacessorio_set'
public = [RP_LIST, RP_DETAIL, ]
class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['data', 'nome', 'tema', 'upload_ata']
ordering = '-data'
class DetailView(MasterDetailCrud.DetailView):
template_name = "comissoes/reuniao_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
docs = []
documentos = DocumentoAcessorio.objects.filter(reuniao=self.kwargs['pk']).order_by('nome')
docs.extend(documentos)
context['docs'] = docs
context['num_docs'] = len(docs)
mats = []
materias_pauta = PautaReuniao.objects.filter(reuniao=self.kwargs['pk'])
materias_pk = [materia_pauta.materia.pk for materia_pauta in materias_pauta]
context['mats'] = MateriaLegislativa.objects.filter(
pk__in=materias_pk
).order_by('tipo', '-ano', '-numero')
context['num_mats'] = len(context['mats'])
context['reuniao_pk'] = self.kwargs['pk']
return context
class ListView(MasterDetailCrud.ListView):
logger = logging.getLogger(__name__)
paginate_by = 10
@ -249,6 +278,100 @@ class ReuniaoCrud(MasterDetailCrud):
return {'comissao': comissao}
class RemovePautaView(PermissionRequiredMixin, CreateView):
model = PautaReuniao
form_class = PautaReuniaoForm
template_name = 'comissoes/pauta.html'
permission_required = ('comissoes.add_reuniao', )
def get_context_data(self, **kwargs):
context = super(
RemovePautaView, self
).get_context_data(**kwargs)
# Remove = 0; Adiciona = 1
context['opcao'] = 0
context['object'] = Reuniao.objects.get(pk=self.kwargs['pk'])
context['root_pk'] = context['object'].comissao.pk
materias_pauta = PautaReuniao.objects.filter(reuniao=context['object'])
materias_pk = [materia_pauta.materia.pk for materia_pauta in materias_pauta]
context['materias'] = MateriaLegislativa.objects.filter(
pk__in=materias_pk
).order_by('tipo', '-ano', '-numero')
context['num_materias'] = len(context['materias'])
return context
def post(self, request, *args, **kwargs):
success_url = reverse('sapl.comissoes:reuniao_detail', kwargs={'pk':kwargs['pk']})
marcadas = request.POST.getlist('materia_id')
if not marcadas:
msg=_('Nenhuma matéria foi selecionada.')
messages.add_message(request, messages.WARNING, msg)
return HttpResponseRedirect(success_url)
reuniao = Reuniao.objects.get(pk=kwargs['pk'])
for materia in MateriaLegislativa.objects.filter(id__in=marcadas):
PautaReuniao.objects.filter(reuniao=reuniao,materia=materia).delete()
msg=_('Matéria(s) removida(s) com sucesso!')
messages.add_message(request, messages.SUCCESS, msg)
return HttpResponseRedirect(success_url)
class AdicionaPautaView(PermissionRequiredMixin, CreateView):
model = PautaReuniao
form_class = PautaReuniaoForm
template_name = 'comissoes/pauta.html'
permission_required = ('comissoes.add_reuniao', )
def get_context_data(self, **kwargs):
context = super(
AdicionaPautaView, self
).get_context_data(**kwargs)
# Adiciona = 1; Remove = 0
context['opcao'] = 1
context['object'] = Reuniao.objects.get(pk=self.kwargs['pk'])
context['root_pk'] = context['object'].comissao.pk
materias_comissao = lista_materias_comissao(context['object'].comissao.pk)
materias_pauta = PautaReuniao.objects.filter(reuniao=context['object'])
nao_listar = [mp.materia.pk for mp in materias_pauta]
context['materias'] = materias_comissao.exclude(pk__in=nao_listar)
context['num_materias'] = len(context['materias'])
return context
def post(self, request, *args, **kwargs):
success_url = reverse('sapl.comissoes:reuniao_detail', kwargs={'pk':kwargs['pk']})
marcadas = request.POST.getlist('materia_id')
if not marcadas:
msg = _('Nenhuma máteria foi selecionada.')
messages.add_message(request, messages.WARNING, msg)
return HttpResponseRedirect(success_url)
reuniao = Reuniao.objects.get(pk=kwargs['pk'])
pautas = []
for materia in MateriaLegislativa.objects.filter(id__in=marcadas):
pauta = PautaReuniao()
pauta.reuniao = reuniao
pauta.materia = materia
pautas.append(pauta)
PautaReuniao.objects.bulk_create(pautas)
msg = _('Matéria(s) adicionada(s) com sucesso!')
messages.add_message(request, messages.SUCCESS, msg)
return HttpResponseRedirect(success_url)
class DocumentoAcessorioCrud(MasterDetailCrud):
model = DocumentoAcessorio
parent_field = 'reuniao__comissao'

29
sapl/materia/migrations/0049_pautareuniao.py

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-05-14 20:11
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('comissoes', '0019_auto_20181214_1023'),
('materia', '0048_merge_20190426_0828'),
]
operations = [
migrations.CreateModel(
name='PautaReuniao',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('materia', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='materia_set', to='materia.MateriaLegislativa', verbose_name='Matéria')),
('reuniao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reuniao_set', to='comissoes.Reuniao', verbose_name='Reunião')),
],
options={
'verbose_name': 'Matéria da Pauta',
'verbose_name_plural': 'Matérias da Pauta',
},
),
]

26
sapl/materia/models.py

@ -11,7 +11,7 @@ from model_utils import Choices
import reversion
from sapl.base.models import SEQUENCIA_NUMERACAO_PROTOCOLO, Autor
from sapl.comissoes.models import Comissao
from sapl.comissoes.models import Comissao, Reuniao
from sapl.compilacao.models import (PerfilEstruturalTextoArticulado,
TextoArticulado)
from sapl.parlamentares.models import Parlamentar
@ -401,6 +401,30 @@ class AcompanhamentoMateria(models.Model):
}
@reversion.register()
class PautaReuniao(models.Model):
reuniao = models.ForeignKey(
Reuniao, related_name='reuniao_set',
on_delete=models.CASCADE,
verbose_name=_('Reunião')
)
materia = models.ForeignKey(
MateriaLegislativa, related_name='materia_set',
verbose_name=_('Matéria')
)
class Meta:
verbose_name = _('Matéria da Pauta')
verbose_name_plural = ('Matérias da Pauta')
def __str__(self):
return _('Reunião: %(reuniao)s'
' - Matéria: %(materia)s') % {
'reuniao': self.reuniao,
'materia': self.materia
}
@reversion.register()
class Anexada(models.Model):
materia_principal = models.ForeignKey(

4
sapl/materia/views.py

@ -2190,8 +2190,8 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
msg = _('Matéria(s) anexada(s).')
messages.add_message(request, messages.SUCCESS, msg)
sucess_url = reverse('sapl_index') + 'materia/' + kwargs['pk'] + '/anexada'
return HttpResponseRedirect(sucess_url)
success_url = reverse('sapl.materia:anexada_list', kwargs={'pk': kwargs['pk']})
return HttpResponseRedirect(success_url)
class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):

2
sapl/protocoloadm/views.py

@ -1090,7 +1090,7 @@ class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView):
msg = _('Documento(s) anexado(s).')
messages.add_message(request, messages.SUCCESS, msg)
success_url = reverse('sapl_index') + 'docadm/' + kwargs['pk'] + '/anexado'
success_url = reverse('sapl.protocoloadm:anexado_list', kwargs={'pk': kwargs['pk']})
return HttpResponseRedirect(success_url)

1
sapl/rules/map_rules.py

@ -103,6 +103,7 @@ rules_group_protocolo = {
rules_group_comissoes = {
'group': SAPL_GROUP_COMISSOES,
'rules': [
(materia.PautaReuniao, __base__, __perms_publicas__),
(comissoes.Comissao, __base__, __perms_publicas__),
(comissoes.Composicao, __base__, __perms_publicas__),
(comissoes.Participacao, __base__, __perms_publicas__),

68
sapl/templates/comissoes/pauta.html

@ -0,0 +1,68 @@
{% extends "crud/detail.html" %}
{% load i18n crispy_forms_tags %}
{% block actions %}{% endblock %}
{% block title %}
<h1 class="page-header">
{% if opcao %}
Adicionar Matérias à Pauta <small>(Reunião: {{object}})</small>
{% else %}
Remover Matérias da Pauta <small>(Reunião: {{object}})</small>
{% endif %}
</h1>
{% endblock %}
{% block detail_content %}
{% if materias %}
{% if num_materias == 1 %}
<b>Há {{num_materias}} matéria disponível.</b> <br><br>
{% else %}
<b>Há {{num_materias}} matérias disponíveis.</b> <br><br>
{% endif %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
<table class="table table-striped table-hover">
<div class="controls">
<div class="checkbox">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(this)" /> Marcar/Desmarcar Todos
</label>
</div>
</div>
<thead>
<tr><th>Matéria</th></tr>
</thead>
<tbody onload="checks(materias_checked)">
{% for materia in materias %}
<tr>
<td>
<input type="checkbox" name="materia_id" value="{{materia.id}}" {% if check %} checked {% endif %}/>
{{materia.tipo.sigla}} {{materia.numero}}/{{materia.ano}} - {{materia.tipo.descricao}}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
{% if opcao %}
<input type="submit" value="Salvar" class="btn btn-primary"S>
{% else %}
<input type="submit" value="Remover" class="btn btn-danger"S>
{% endif %}
</form>
{% else %}
<b>Não há matéria disponível.</b> <br><br>
{% endif %}
{% endblock %}
{% block extra_js %}
<script language="JavaScript">
function checkAll(elem){
let checkboxes = document.getElementsByName('materia_id');
for(let i=0; i<checkboxes.length; i++){
if(checkboxes[i].type == 'checkbox')
checkboxes[i].checked = elem.checked
}
}
</script>
{% endblock %}

65
sapl/templates/comissoes/reuniao_detail.html

@ -0,0 +1,65 @@
{% extends "crud/detail.html" %}
{% load i18n %}
{% block detail_content %}
{{ block.super }}
<h2 class="legend">Pauta</h2>
{% if mats %}
<p>Total de Registros: <b>{{num_mats}}</b></p>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Matéria</th>
</tr>
</thead>
<tbody>
{% for mat in mats %}
<tr>
<td>
<a href="{% url 'sapl.materia:materialegislativa_detail' mat.pk %}">{{mat}}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if perms.comissoes.add_reuniao %}
<div class="actions btn-group float-right" role="group">
<a href="{% url 'sapl.comissoes:pauta_add' reuniao_pk %}" class="btn btn-outline-primary">{% trans 'Adicionar Matéria' %}</a>
<a href="{% url 'sapl.comissoes:pauta_remove' reuniao_pk %}" class="btn btn-outline-primary btn-outline-danger">{% trans 'Remover Matéria' %}</a>
</div>
{% endif %}
{% else %}
{% if perms.comissoes.add_reuniao %}
<a href="{% url 'sapl.comissoes:pauta_add' reuniao_pk %}" class="btn btn-outline-primary">{% trans 'Adicionar Matéria' %}</a>
{% endif %}
{% endif %}
<br /><br />
<h2 class="legend">Documentos Acessórios</h2>
{% if docs %}
<p>Total de registros: <b>{{num_docs}}</b></p>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Documento Acessório</th>
</tr>
</thead>
<tbody>
{% for doc in docs %}
<tr>
<td>
<a href="{% url 'sapl.comissoes:documentoacessorio_detail' doc.pk %}">{{ doc.nome }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if perms.comissoes.add_reuniao %}
<a href="{% url 'sapl.comissoes:documentoacessorio_create' reuniao_pk %}" class="btn btn-outline-primary float-right">{% trans 'Adicionar Documento' %}</a>
{% endif %}
{% else %}
{% if perms.comissoes.add_reuniao %}
<a href="{% url 'sapl.comissoes:documentoacessorio_create' reuniao_pk %}" class="btn btn-outline-primary">{% trans 'Adicionar Documento' %}</a>
{% endif %}
{% endif %}
<br /><br />
{% endblock detail_content %}
Loading…
Cancel
Save