Browse Source

Reunião para frente parlamentar

Reuniao_frente_3364
Gustavo274 3 years ago
parent
commit
1f60147f41
  1. 28
      sapl/materia/migrations/0080_pautareuniaofrente.py
  2. 26
      sapl/materia/models.py
  3. 192
      sapl/parlamentares/forms.py
  4. 60
      sapl/parlamentares/migrations/0039_documentoacessorio_reuniao.py
  5. 186
      sapl/parlamentares/models.py
  6. 36
      sapl/parlamentares/urls.py
  7. 318
      sapl/parlamentares/views.py
  8. 3
      sapl/templates/parlamentares/frente_detail.html
  9. 24
      sapl/templates/parlamentares/layouts.yaml
  10. 33
      sapl/templates/parlamentares/materias_em_tramitacao.html
  11. 113
      sapl/templates/parlamentares/pauta.html
  12. 59
      sapl/templates/parlamentares/reuniao_detail.html

28
sapl/materia/migrations/0080_pautareuniaofrente.py

@ -0,0 +1,28 @@
# Generated by Django 2.2.20 on 2021-08-17 23:29
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0039_documentoacessorio_reuniao'),
('materia', '0079_auto_20210311_1711'),
]
operations = [
migrations.CreateModel(
name='PautaReuniaoFrente',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('materia', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='materia.MateriaLegislativa', verbose_name='Matéria')),
('reuniao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parlamentares.Reuniao', verbose_name='Reunião')),
],
options={
'verbose_name': 'Matéria da Pauta',
'verbose_name_plural': 'Matérias da Pauta',
'ordering': ('id',),
},
),
]

26
sapl/materia/models.py

@ -10,6 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
import reversion
from sapl.parlamentares.models import Reuniao as ReuniaoFrente
from sapl.base.models import SEQUENCIA_NUMERACAO_PROTOCOLO, Autor
from sapl.comissoes.models import Comissao, Reuniao
from sapl.compilacao.models import (PerfilEstruturalTextoArticulado,
@ -462,6 +463,31 @@ class PautaReuniao(models.Model):
}
class PautaReuniaoFrente(models.Model):
reuniao = models.ForeignKey(
ReuniaoFrente,
on_delete=models.CASCADE,
verbose_name=_('Reunião')
)
materia = models.ForeignKey(
MateriaLegislativa,
on_delete=models.PROTECT,
verbose_name=_('Matéria')
)
class Meta:
verbose_name = _('Matéria da Pauta')
verbose_name_plural = ('Matérias da Pauta')
ordering = ('id',)
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(

192
sapl/parlamentares/forms.py

@ -1,5 +1,6 @@
from datetime import timedelta
import logging
from sapl.materia.models import MateriaEmTramitacao, PautaReuniaoFrente
from sapl.crispy_layout_mixin import SaplFormHelper
from crispy_forms.layout import Fieldset, Layout
@ -15,15 +16,15 @@ from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from floppyforms.widgets import ClearableFileInput
from image_cropping.widgets import CropWidget, ImageCropWidget
from sapl.utils import FileFieldCheckMixin
from sapl.utils import FileFieldCheckMixin, FilterOverridesMetaMixin, validar_arquivo
from sapl.base.models import Autor, TipoAutor
from sapl.crispy_layout_mixin import form_actions, to_row
from sapl.rules import SAPL_GROUP_VOTANTE
import django_filters
from .models import (Coligacao, ComposicaoColigacao, Filiacao, Frente, Legislatura,
Mandato, Parlamentar, Partido, Votante, Bloco, FrenteParlamentar, BlocoMembro)
from .models import (Coligacao, ComposicaoColigacao, DocumentoAcessorio, Filiacao, Frente, Legislatura,
Mandato, Parlamentar, Partido, Reuniao, Votante, Bloco, FrenteParlamentar, BlocoMembro)
class ImageThumbnailFileInput(ClearableFileInput):
@ -253,7 +254,8 @@ class ParlamentarFilterSet(django_filters.FilterSet):
class ColigacaoFilterSet(django_filters.FilterSet):
nome = django_filters.CharFilter(label=_('Nome da Coligação'), lookup_expr='icontains')
nome = django_filters.CharFilter(
label=_('Nome da Coligação'), lookup_expr='icontains')
class Meta:
model = Coligacao
@ -316,7 +318,8 @@ class ParlamentarCreateForm(ParlamentarForm):
return self.cleaned_data
cleaned_data = self.cleaned_data
parlamentar = Parlamentar.objects.filter(nome_parlamentar=cleaned_data['nome_parlamentar']).exists()
parlamentar = Parlamentar.objects.filter(nome_parlamentar=cleaned_data[
'nome_parlamentar']).exists()
if parlamentar:
self.logger.error('Parlamentar já cadastrado.')
@ -537,9 +540,11 @@ class FrenteParlamentarForm(ModelForm):
return self.cleaned_data
if cd['cargo'].cargo_unico:
frente_parlamentar = FrenteParlamentar.objects.filter(frente=cd['frente'], cargo=cd['cargo'])
frente_parlamentar = FrenteParlamentar.objects.filter(
frente=cd['frente'], cargo=cd['cargo'])
if frente_parlamentar and not frente_parlamentar[0].parlamentar == cd['parlamentar']:
raise ValidationError(_("Cargo único já ocupado por outro parlamentar."))
raise ValidationError(
_("Cargo único já ocupado por outro parlamentar."))
return cd
@ -669,11 +674,15 @@ class VincularParlamentarForm(forms.Form):
data_expedicao_diploma = cleaned_data['data_expedicao_diploma']
if parlamentar.mandato_set.filter(legislatura=legislatura):
self.logger.error('Parlamentar já está vinculado a legislatura informada.')
raise ValidationError(_('Parlamentar já está vinculado a legislatura informada.'))
self.logger.error(
'Parlamentar já está vinculado a legislatura informada.')
raise ValidationError(
_('Parlamentar já está vinculado a legislatura informada.'))
elif data_expedicao_diploma and legislatura.data_inicio <= data_expedicao_diploma:
self.logger.error('Data da Expedição do Diploma deve ser anterior a data de início da Legislatura.')
raise ValidationError(_('Data da Expedição do Diploma deve ser anterior a data de início da Legislatura.'))
self.logger.error(
'Data da Expedição do Diploma deve ser anterior a data de início da Legislatura.')
raise ValidationError(
_('Data da Expedição do Diploma deve ser anterior a data de início da Legislatura.'))
return cleaned_data
@ -736,8 +745,165 @@ class BlocoMembroForm(ModelForm):
if cd['cargo'].cargo_unico \
and BlocoMembro.objects.filter(bloco=cd['bloco'], cargo=cd['cargo'], data_saida__isnull=True)\
.exclude(pk=self.instance.pk).exists():
raise ValidationError(_("Cargo único já ocupado por outro membro."))
raise ValidationError(
_("Cargo único já ocupado por outro membro."))
elif not cd['data_saida'] and BlocoMembro.objects.filter(parlamentar=cd['parlamentar'], data_saida__isnull=True).exists():
raise ValidationError(_("Parlamentar já é membro do bloco parlamentar."))
raise ValidationError(
_("Parlamentar já é membro do bloco parlamentar."))
return cd
class ReuniaoForm(ModelForm):
logger = logging.getLogger(__name__)
frente = forms.ModelChoiceField(queryset=Frente.objects.all(),
widget=forms.HiddenInput())
class Meta:
model = Reuniao
exclude = ['cod_andamento_reuniao']
def clean(self):
super(ReuniaoForm, self).clean()
if not self.is_valid():
return self.cleaned_data
if self.cleaned_data['hora_fim']:
if (self.cleaned_data['hora_fim'] <
self.cleaned_data['hora_inicio']):
msg = _(
'A hora de término da reunião não pode ser menor que a de início')
self.logger.warning(
"A hora de término da reunião ({}) não pode ser menor que a de início ({}).".format(
self.cleaned_data[
'hora_fim'], self.cleaned_data['hora_inicio']
)
)
raise ValidationError(msg)
upload_pauta = self.cleaned_data.get('upload_pauta', False)
upload_ata = self.cleaned_data.get('upload_ata', False)
upload_anexo = self.cleaned_data.get('upload_anexo', False)
if upload_pauta:
validar_arquivo(upload_pauta, "Pauta da Reunião")
if upload_ata:
validar_arquivo(upload_ata, "Ata da Reunião")
if upload_anexo:
validar_arquivo(upload_anexo, "Anexo da Reunião")
return self.cleaned_data
class PautaReuniaoFilterSet(django_filters.FilterSet):
class Meta(FilterOverridesMetaMixin):
model = MateriaEmTramitacao
fields = ['materia__tipo', 'materia__ano',
'materia__numero', 'materia__data_apresentacao']
def __init__(self, *args, **kwargs):
super(PautaReuniaoFilterSet, self).__init__(*args, **kwargs)
self.filters['materia__tipo'].label = "Tipo da Matéria"
self.filters['materia__ano'].label = "Ano da Matéria"
self.filters['materia__numero'].label = "Número da Matéria"
self.filters[
'materia__data_apresentacao'].label = "Data (Inicial - Final)"
row1 = to_row(
[('materia__tipo', 4), ('materia__ano', 4), ('materia__numero', 4)])
row2 = to_row([('materia__data_apresentacao', 12)])
self.form.helper = SaplFormHelper()
self.form.helper.form_method = "GET"
self.form.helper.layout = Layout(
Fieldset(
_("Pesquisa de Matérias"), row1, row2,
form_actions(label="Pesquisar")
)
)
class PautaReuniaoForm(forms.ModelForm):
class Meta:
model = PautaReuniaoFrente
exclude = ['reuniao']
class DocumentoAcessorioCreateForm(FileFieldCheckMixin, forms.ModelForm):
parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput())
class Meta:
model = DocumentoAcessorio
exclude = ['reuniao']
def __init__(self, user=None, **kwargs):
super(DocumentoAcessorioCreateForm, self).__init__(**kwargs)
if self.instance:
reuniao = Reuniao.objects.get(id=self.initial['parent_pk'])
frente = reuniao.frente
frente_pk = frente.id
documentos = reuniao.documentoacessorio_set.all()
return self.create_documentoacessorio()
def create_documentoacessorio(self):
reuniao = Reuniao.objects.get(id=self.initial['parent_pk'])
def clean(self):
super(DocumentoAcessorioCreateForm, self).clean()
if not self.is_valid():
return self.cleaned_data
arquivo = self.cleaned_data.get('arquivo')
if arquivo:
validar_arquivo(arquivo, "Texto Integral")
# else:
# ## TODO: definir arquivo no form e preservar o nome do campo
# ## que gerou a mensagem de erro.
# ## arquivo = forms.FileField(required=True, label="Texto Integral")
# nome_arquivo = self.fields['arquivo'].label
# raise ValidationError(f'Favor anexar arquivo em {nome_arquivo}')
return self.cleaned_data
class DocumentoAcessorioEditForm(FileFieldCheckMixin, forms.ModelForm):
parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput())
class Meta:
model = DocumentoAcessorio
fields = ['nome', 'data', 'autor', 'ementa',
'indexacao', 'arquivo']
def __init__(self, user=None, **kwargs):
super(DocumentoAcessorioEditForm, self).__init__(**kwargs)
def clean(self):
super(DocumentoAcessorioEditForm, self).clean()
if not self.is_valid():
return self.cleaned_data
arquivo = self.cleaned_data.get('arquivo')
if arquivo:
validar_arquivo(arquivo, "Texto Integral")
# else:
# ## TODO: definir arquivo no form e preservar o nome do campo
# ## que gerou a mensagem de erro.
# ## arquivo = forms.FileField(required=True, label="Texto Integral")
# nome_arquivo = self.fields['arquivo'].label
# raise ValidationError(f'Favor anexar arquivo em {nome_arquivo}')
return self.cleaned_data

60
sapl/parlamentares/migrations/0039_documentoacessorio_reuniao.py

@ -0,0 +1,60 @@
# Generated by Django 2.2.20 on 2021-08-17 23:26
from django.db import migrations, models
import django.db.models.deletion
import sapl.parlamentares.models
import sapl.utils
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0038_sessao_legislativa_redundante'),
]
operations = [
migrations.CreateModel(
name='Reuniao',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('numero', models.PositiveIntegerField(verbose_name='Número')),
('nome', models.CharField(max_length=150, verbose_name='Nome da Reunião')),
('tema', models.CharField(blank=True, max_length=150, verbose_name='Tema da Reunião')),
('data', models.DateField(verbose_name='Data')),
('hora_inicio', models.TimeField(null=True, verbose_name='Horário de Início (hh:mm)')),
('hora_fim', models.TimeField(blank=True, null=True, verbose_name='Horário de Término (hh:mm)')),
('local_reuniao', models.CharField(blank=True, max_length=100, verbose_name='Local da Reunião')),
('observacao', models.TextField(blank=True, verbose_name='Observação')),
('url_audio', models.URLField(blank=True, max_length=150, verbose_name='URL do Arquivo de Áudio (Formatos MP3 / AAC)')),
('url_video', models.URLField(blank=True, max_length=150, verbose_name='URL do Arquivo de Vídeo (Formatos MP4 / FLV / WebM)')),
('upload_pauta', models.FileField(blank=True, max_length=300, null=True, storage=sapl.utils.OverwriteStorage(), upload_to=sapl.parlamentares.models.pauta_upload_path, verbose_name='Pauta da Reunião')),
('upload_ata', models.FileField(blank=True, max_length=300, null=True, storage=sapl.utils.OverwriteStorage(), upload_to=sapl.parlamentares.models.ata_upload_path, verbose_name='Ata da Reunião')),
('upload_anexo', models.FileField(blank=True, max_length=300, null=True, storage=sapl.utils.OverwriteStorage(), upload_to=sapl.parlamentares.models.anexo_upload_path, verbose_name='Anexo da Reunião')),
('frente', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parlamentares.Frente', verbose_name='Frente Parlamentar')),
],
options={
'verbose_name': 'Reunião de Frente Parlamentar',
'verbose_name_plural': 'Reuniões de Frentes Parlamentares',
'ordering': ('numero', 'frente'),
},
),
migrations.CreateModel(
name='DocumentoAcessorio',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nome', models.CharField(max_length=50, verbose_name='Nome')),
('data', models.DateField(blank=True, default=None, null=True, verbose_name='Data')),
('autor', models.CharField(max_length=200, verbose_name='Autor')),
('ementa', models.TextField(blank=True, verbose_name='Ementa')),
('indexacao', models.TextField(blank=True)),
('arquivo', models.FileField(blank=True, max_length=300, null=True, storage=sapl.utils.OverwriteStorage(), upload_to=sapl.parlamentares.models.anexo_upload_path, verbose_name='Texto Integral')),
('data_ultima_atualizacao', models.DateTimeField(auto_now=True, null=True, verbose_name='Data')),
('reuniao', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='documentoacessorio_set', to='parlamentares.Reuniao')),
],
options={
'verbose_name': 'Documento Acessório',
'verbose_name_plural': 'Documentos Acessórios',
'ordering': ('data', 'id'),
},
),
]

186
sapl/parlamentares/models.py

@ -1,14 +1,17 @@
import os
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from image_cropping.fields import ImageCropField, ImageRatioField
from model_utils import Choices
import reversion
import magic
from sapl.base.models import Autor
from sapl.decorators import vigencia_atual
from sapl.utils import (LISTA_DE_UFS, YES_NO_CHOICES, SaplGenericRelation,
from sapl.utils import (LISTA_DE_UFS, OverwriteStorage, YES_NO_CHOICES, SaplGenericRelation,
get_settings_auth_user_model,
intervalos_tem_intersecao,
restringe_tipos_de_arquivo_img, texto_upload_path)
@ -496,6 +499,7 @@ class CargoMesa(models.Model):
def __str__(self):
return self.descricao
@reversion.register()
class MesaDiretora(models.Model):
data_inicio = models.DateField(verbose_name=_('Data Início'), null=True)
@ -511,7 +515,7 @@ class MesaDiretora(models.Model):
def __str__(self):
return _('Mesa da %(sessao)s sessao da %(legislatura)s Legislatura') % {
'sessao':self.sessao_legislativa, 'legislatura':self.sessao_legislativa.legislatura
'sessao': self.sessao_legislativa, 'legislatura': self.sessao_legislativa.legislatura
}
@ -520,7 +524,8 @@ class ComposicaoMesa(models.Model):
# TODO M2M ???? Ternary?????
parlamentar = models.ForeignKey(Parlamentar, on_delete=models.PROTECT)
cargo = models.ForeignKey(CargoMesa, on_delete=models.PROTECT)
mesa_diretora = models.ForeignKey(MesaDiretora, on_delete=models.PROTECT, null=True)
mesa_diretora = models.ForeignKey(
MesaDiretora, on_delete=models.PROTECT, null=True)
class Meta:
verbose_name = _('Ocupação de cargo na Mesa')
@ -532,6 +537,7 @@ class ComposicaoMesa(models.Model):
'parlamentar': self.parlamentar, 'cargo': self.cargo
}
@reversion.register()
class Frente(models.Model):
'''
@ -732,3 +738,177 @@ class BlocoMembro(models.Model):
def __str__(self):
return f"{self.parlamentar} - {self.cargo.nome_cargo} - {self.bloco}"
def pauta_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='pauta', pk_first=True)
def ata_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='ata', pk_first=True)
def anexo_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='anexo', pk_first=True)
class Reuniao(models.Model):
frente = models.ForeignKey(
Frente,
on_delete=models.CASCADE,
verbose_name=_('Frente Parlamentar'))
numero = models.PositiveIntegerField(verbose_name=_('Número'))
nome = models.CharField(
max_length=150, verbose_name=_('Nome da Reunião'))
tema = models.CharField(
max_length=150, blank=True, verbose_name=_('Tema da Reunião'))
data = models.DateField(verbose_name=_('Data'))
hora_inicio = models.TimeField(
null=True,
verbose_name=_('Horário de Início (hh:mm)'))
hora_fim = models.TimeField(
blank=True,
null=True,
verbose_name=_('Horário de Término (hh:mm)'))
local_reuniao = models.CharField(
max_length=100, blank=True, verbose_name=_('Local da Reunião'))
observacao = models.TextField(
blank=True, verbose_name=_('Observação'))
url_audio = models.URLField(
max_length=150, blank=True,
verbose_name=_('URL do Arquivo de Áudio (Formatos MP3 / AAC)'))
url_video = models.URLField(
max_length=150, blank=True,
verbose_name=_('URL do Arquivo de Vídeo (Formatos MP4 / FLV / WebM)'))
upload_pauta = models.FileField(
max_length=300,
blank=True, null=True,
upload_to=pauta_upload_path,
verbose_name=_('Pauta da Reunião'),
storage=OverwriteStorage())
upload_ata = models.FileField(
max_length=300,
blank=True, null=True,
upload_to=ata_upload_path,
verbose_name=_('Ata da Reunião'),
storage=OverwriteStorage())
upload_anexo = models.FileField(
max_length=300,
blank=True, null=True,
upload_to=anexo_upload_path,
storage=OverwriteStorage(),
verbose_name=_('Anexo da Reunião'))
class Meta:
verbose_name = _('Reunião de Frente Parlamentar')
verbose_name_plural = _('Reuniões de Frentes Parlamentares')
ordering = ('numero', 'frente')
def __str__(self):
return self.nome
def delete(self, using=None, keep_parents=False):
upload_pauta = self.upload_pauta
upload_ata = self.upload_ata
upload_anexo = self.upload_anexo
result = super().delete(using=using, keep_parents=keep_parents)
if upload_pauta:
upload_pauta.delete(save=False)
if upload_ata:
upload_ata.delete(save=False)
if upload_anexo:
upload_anexo.delete(save=False)
return result
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if not self.pk and (self.upload_pauta or self.upload_ata or
self.upload_anexo):
upload_pauta = self.upload_pauta
upload_ata = self.upload_ata
upload_anexo = self.upload_anexo
self.upload_pauta = None
self.upload_ata = None
self.upload_anexo = None
models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
self.upload_pauta = upload_pauta
self.upload_ata = upload_ata
self.upload_anexo = upload_anexo
return models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
class DocumentoAcessorio(models.Model):
reuniao = models.ForeignKey(Reuniao,
related_name='documentoacessorio_set',
on_delete=models.PROTECT)
nome = models.CharField(max_length=50, verbose_name=_('Nome'))
data = models.DateField(blank=True, null=True,
default=None, verbose_name=_('Data'))
autor = models.CharField(
max_length=200, verbose_name=_('Autor'))
ementa = models.TextField(blank=True, verbose_name=_('Ementa'))
indexacao = models.TextField(blank=True)
arquivo = models.FileField(
max_length=300,
blank=True,
null=True,
upload_to=anexo_upload_path,
verbose_name=_('Texto Integral'),
storage=OverwriteStorage())
data_ultima_atualizacao = models.DateTimeField(
blank=True, null=True,
auto_now=True,
verbose_name=_('Data'))
class Meta:
verbose_name = _('Documento Acessório')
verbose_name_plural = _('Documentos Acessórios')
ordering = ('data', 'id')
def __str__(self):
return _('%(nome)s por %(autor)s') % {
'nome': self.nome,
'autor': self.autor}
def delete(self, using=None, keep_parents=False):
arquivo = self.arquivo
result = super().delete(using=using, keep_parents=keep_parents)
if arquivo:
arquivo.delete(save=False)
return result
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if not self.pk and self.arquivo:
arquivo = self.arquivo
self.arquivo = None
models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)
self.arquivo = arquivo
return models.Model.save(self, force_insert=force_insert,
force_update=force_update,
using=using,
update_fields=update_fields)

36
sapl/parlamentares/urls.py

@ -1,6 +1,7 @@
from sapl.parlamentares.models import DocumentoAcessorio
from django.conf.urls import include, url
from sapl.parlamentares.views import (CargoMesaCrud, ColigacaoCrud,
from sapl.parlamentares.views import (AdicionaPautaView, CargoMesaCrud, ColigacaoCrud, DocumentoAcessorioCrud, RemovePautaView, ReuniaoFrenteCrud,
coligacao_legislatura,
ComposicaoColigacaoCrud, DependenteCrud,
FiliacaoCrud, FrenteCrud, FrenteList,
@ -45,24 +46,37 @@ urlpatterns = [
url(r'^parlamentar/(?P<pk>\d+)/materias$',
ParlamentarMateriasView.as_view(), name='parlamentar_materias'),
url(r'^parlamentar/(?P<pk>\d+)/frentes/$', get_parlamentar_frentes, name='parlamentar_frentes'),
url(r'^parlamentar/(?P<pk>\d+)/frentes/$',
get_parlamentar_frentes, name='parlamentar_frentes'),
url(r'^parlamentar/vincular-parlamentar/$',
VincularParlamentarView.as_view(), name='vincular_parlamentar'),
url(r'^parlamentar/coligacao-legislatura/', coligacao_legislatura, name="coligacao_legislatura"),
url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() + ComposicaoColigacaoCrud.get_urls())),
url(r'^sistema/pesquisar-coligacao/', PesquisarColigacaoView.as_view(), name='pesquisar_coligacao'),
url(r'^parlamentar/coligacao-legislatura/',
coligacao_legislatura, name="coligacao_legislatura"),
url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() +
ComposicaoColigacaoCrud.get_urls())),
url(r'^sistema/pesquisar-coligacao/',
PesquisarColigacaoView.as_view(), name='pesquisar_coligacao'),
url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() + ComposicaoColigacaoCrud.get_urls())),
url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() +
ComposicaoColigacaoCrud.get_urls())),
url(r'^sistema/bloco/', include(BlocoCrud.get_urls())),
url(r'^sistema/bloco-cargo/', include(BlocoCargoCrud.get_urls())),
url(r'^sistema/bloco-membros/', include(BlocoMembroCrud.get_urls())),
url(r'^sistema/frente/', include(FrenteCrud.get_urls())),
url(r'^sistema/frente/',
include(FrenteCrud.get_urls() + ReuniaoFrenteCrud.get_urls() + DocumentoAcessorioCrud.get_urls())),
url(r'^sistema/frente/(?P<pk>\d+)/pauta/add',
AdicionaPautaView.as_view(), name='pauta_add'),
url(r'^sistema/frente/(?P<pk>\d+)/pauta/remove',
RemovePautaView.as_view(), name='pauta_remove'),
url(r'^sistema/frente-cargo/', include(FrenteCargoCrud.get_urls())),
url(r'^sistema/frente-parlamentares/', include(FrenteParlamentarCrud.get_urls())),
url(r'^sistema/frente-parlamentares/',
include(FrenteParlamentarCrud.get_urls())),
url(r'^sistema/frente/atualiza-lista-parlamentares',
frente_atualiza_lista_parlamentares,
@ -83,8 +97,10 @@ urlpatterns = [
include(TipoMilitarCrud.get_urls())),
url(r'^sistema/parlamentar/partido/', include(PartidoCrud.get_urls())),
url(r'^sistema/parlamentar/pesquisar-partido/', PesquisarPartidoView.as_view(), name='pesquisar_partido'),
url(r'^sistema/parlamentar/partido/(?P<pk>\d+)/filiados$', parlamentares_filiados, name='parlamentares_filiados'),
url(r'^sistema/parlamentar/pesquisar-partido/',
PesquisarPartidoView.as_view(), name='pesquisar_partido'),
url(r'^sistema/parlamentar/partido/(?P<pk>\d+)/filiados$',
parlamentares_filiados, name='parlamentares_filiados'),
url(r'^sistema/mesa-diretora/sessao-legislativa/',
include(SessaoLegislativaCrud.get_urls())),

318
sapl/parlamentares/views.py

@ -19,7 +19,8 @@ from django.utils.datastructures import MultiValueDictKeyError
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import FormView
from django.views.generic.edit import UpdateView
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.list import ListView
from django_filters.views import FilterView
from image_cropping.utils import get_backend
@ -29,18 +30,18 @@ from sapl.comissoes.models import Participacao
from sapl.crud.base import (RP_CHANGE, RP_DETAIL, RP_LIST, Crud, CrudAux,
CrudBaseForListAndDetailExternalAppView,
MasterDetailCrud, make_pagination)
from sapl.materia.models import Autoria, Proposicao, Relatoria
from sapl.materia.models import Autoria, MateriaEmTramitacao, MateriaLegislativa, PautaReuniaoFrente, Proposicao, Relatoria
from sapl.parlamentares.apps import AppConfig
from sapl.rules import SAPL_GROUP_VOTANTE
from sapl.utils import (parlamentares_ativos, show_results_filter_set)
from .forms import (ColigacaoFilterSet, FiliacaoForm, FrenteForm, LegislaturaForm, MandatoForm,
ParlamentarCreateForm, ParlamentarForm, VotanteForm,
from .forms import (ColigacaoFilterSet, DocumentoAcessorioCreateForm, DocumentoAcessorioEditForm, FiliacaoForm, FrenteForm, LegislaturaForm, MandatoForm,
ParlamentarCreateForm, ParlamentarForm, PautaReuniaoFilterSet, PautaReuniaoForm, ReuniaoForm, VotanteForm,
ParlamentarFilterSet, PartidoFilterSet, VincularParlamentarForm,
BlocoForm, FrenteParlamentarForm, BlocoMembroForm)
from .models import (CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa,
Dependente, Filiacao, Frente, Legislatura, Mandato,
NivelInstrucao, Parlamentar, Partido, SessaoLegislativa,
Dependente, DocumentoAcessorio, Filiacao, Frente, Legislatura, Mandato,
NivelInstrucao, Parlamentar, Partido, Reuniao, SessaoLegislativa,
SituacaoMilitar, TipoAfastamento, TipoDependente, Votante,
Bloco, FrenteCargo, FrenteParlamentar, BlocoCargo, BlocoMembro, MesaDiretora)
@ -57,6 +58,13 @@ DependenteCrud = MasterDetailCrud.build(
Dependente, 'parlamentar', 'dependente')
def pegar_url_reuniao(pk):
documentoacessorio = DocumentoAcessorio.objects.get(id=pk)
r_pk = documentoacessorio.reuniao.pk
url = reverse('sapl.parlamentares:reuniao_detail', kwargs={'pk': r_pk})
return url
class SessaoLegislativaCrud(CrudAux):
model = SessaoLegislativa
@ -77,6 +85,7 @@ class PartidoCrud(CrudAux):
form_class = PartidoForm
class DeleteView(CrudAux.DeleteView):
def get_success_url(self):
return reverse('sapl.parlamentares:pesquisar_partido')
@ -417,6 +426,7 @@ class ColigacaoCrud(CrudAux):
return context
class DeleteView(CrudAux.DeleteView):
def get_success_url(self):
return reverse('sapl.parlamentares:pesquisar_coligacao')
@ -498,6 +508,7 @@ class FrenteCrud(Crud):
list_field_names = ['nome', 'data_criacao', 'data_extincao']
class BaseMixin(Crud.BaseMixin):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['subnav_template_name'] = ''
@ -540,6 +551,7 @@ class FrenteParlamentarCrud(MasterDetailCrud):
return context
class DetailView(MasterDetailCrud.DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['subnav_template_name'] = ''
@ -971,7 +983,7 @@ def altera_field_mesa(request):
operação (Legislatura/Sessão/Inclusão/Remoção),
atualizando os campos após cada alteração
"""
#TODO: Adicionar opção de selecionar mesa diretora no CRUD
# TODO: Adicionar opção de selecionar mesa diretora no CRUD
logger = logging.getLogger(__name__)
legislatura = request.GET['legislatura']
@ -1003,20 +1015,23 @@ def altera_field_mesa(request):
mesa_diretora = request.GET.get('mesa_diretora')
#Mesa nao deve ser informada ainda
# Mesa nao deve ser informada ainda
if not mesa_diretora:
#Cria nova mesa diretora ou retorna a primeira
mesa_diretora, _ = MesaDiretora.objects.get_or_create(sessao_legislativa=sessao_selecionada)
# Cria nova mesa diretora ou retorna a primeira
mesa_diretora, _ = MesaDiretora.objects.get_or_create(
sessao_legislativa=sessao_selecionada)
#TODO: quando a mesa for criada explicitamente em tabelas auxiliares,
# TODO: quando a mesa for criada explicitamente em tabelas auxiliares,
# deve-se somente tentar recuperar a mesa, e caso nao exista
# retornar o erro abaixo
# return JsonResponse({'msg': ('Nenhuma mesa encontrada na sessão!')})
else:
try:
mesa_diretora = MesaDiretora.objects.get(id=mesa_diretora, sessao_legislativa=sessao_selecionada)
mesa_diretora = MesaDiretora.objects.get(
id=mesa_diretora, sessao_legislativa=sessao_selecionada)
except ObjectDoesNotExist:
mesa_diretora = MesaDiretora.objects.filter(sessao_legislativa=sessao_selecionada).first()
mesa_diretora = MesaDiretora.objects.filter(
sessao_legislativa=sessao_selecionada).first()
# Atualiza os componentes da view após a mudança
composicao_mesa = ComposicaoMesa.objects.select_related('cargo', 'parlamentar').filter(
@ -1064,9 +1079,10 @@ def insere_parlamentar_composicao(request):
composicao = ComposicaoMesa()
try:
#logger.debug(
# logger.debug(
# "user=" + username + ". Tentando obter SessaoLegislativa com id={}.".format(request.POST['sessao']))
mesa_diretora, _ = MesaDiretora.objects.get_or_create(sessao_legislativa_id=int(request.POST['sessao']))
mesa_diretora, _ = MesaDiretora.objects.get_or_create(
sessao_legislativa_id=int(request.POST['sessao']))
composicao.mesa_diretora = mesa_diretora
except MultiValueDictKeyError:
logger.error(
@ -1204,7 +1220,7 @@ def altera_field_mesa_public_view(request):
atualizando os campos após cada alteração
"""
#TODO: Adicionar opção de selecionar mesa diretora no CRUD
# TODO: Adicionar opção de selecionar mesa diretora no CRUD
logger = logging.getLogger(__name__)
username = request.user.username
@ -1234,25 +1250,28 @@ def altera_field_mesa_public_view(request):
"Selecionado o id da primeira sessão.")
sessao_selecionada = sessoes.first()
else:
sessao_selecionada = SessaoLegislativa.objects.get(id=sessao_selecionada)
sessao_selecionada = SessaoLegislativa.objects.get(
id=sessao_selecionada)
# Atualiza os componentes da view após a mudança
lista_sessoes = [(s.id, s.__str__()) for s in sessoes]
#Pegar Mesas diretoras da sessao
# Pegar Mesas diretoras da sessao
mesa_diretora = request.GET.get('mesa_diretora')
#Mesa nao deve ser informada ainda
# Mesa nao deve ser informada ainda
if not mesa_diretora:
try:
mesa_diretora = sessao_selecionada.mesadiretora_set.first()
except ObjectDoesNotExist:
logger.error(f"user={username}. Mesa não encontrada com sessão Nº {sessao_selecionada.id}. ")
logger.error(
f"user={username}. Mesa não encontrada com sessão Nº {sessao_selecionada.id}. ")
else:
#Cria nova mesa diretora ou retorna a primeira
mesa_diretora, _ = MesaDiretora.objects.get_or_create(sessao_legislativa=sessao_selecionada)
# Cria nova mesa diretora ou retorna a primeira
mesa_diretora, _ = MesaDiretora.objects.get_or_create(
sessao_legislativa=sessao_selecionada)
#TODO: quando a mesa for criada explicitamente em tabelas auxiliares,
# TODO: quando a mesa for criada explicitamente em tabelas auxiliares,
# deve-se somente tentar recuperar a mesa, e caso nao exista
# retornar o erro abaixo
# logger.error(f"user={username}. Mesa Nº {mesa_diretora} não encontrada na sessão Nº {sessao_selecionada.id}. "
@ -1299,7 +1318,7 @@ def altera_field_mesa_public_view(request):
'lista_sessoes': lista_sessoes,
'lista_fotos': lista_fotos,
'sessao_selecionada': sessao_selecionada.id,
'mesa_diretora':mesa_diretora.id,
'mesa_diretora': mesa_diretora.id,
'msg': ('', 1)
})
@ -1376,6 +1395,7 @@ class BlocoMembroCrud(MasterDetailCrud):
return context
class DetailView(MasterDetailCrud.DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['subnav_template_name'] = ''
@ -1400,3 +1420,253 @@ def get_sessoes_legislatura(request):
json_response['sessoes_legislativas'].append((s.id, str(s)))
return JsonResponse(json_response)
def lista_materias_frente(frente_pk):
materias = MateriaEmTramitacao.objects.filter(
tramitacao__unidade_tramitacao_destino__frente=frente_pk
).order_by('materia__tipo', '-materia__ano', '-materia__numero')
return materias
class MateriasTramitacaoListView(ListView):
template_name = "parlamentares/materias_em_tramitacao.html"
paginate_by = 10
def get_queryset(self):
return list(lista_materias_frente(self.kwargs['pk']))
def get_context_data(self, **kwargs):
context = super(
MateriasTramitacaoListView, self).get_context_data(**kwargs)
context['object'] = Frente.objects.get(id=self.kwargs['pk'])
context['qtde'] = len(self.object_list)
return context
class ReuniaoFrenteCrud(MasterDetailCrud):
model = Reuniao
parent_field = 'frente'
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 = "parlamentares/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 = PautaReuniaoFrente.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
def take_reuniao_pk(self):
username = self.request.user.username
try:
self.logger.debug('user=' + username +
'. Tentando obter pk da reunião.')
return int(self.request.GET['pk'])
except Exception as e:
self.logger.error(
'user=' + username + '. Erro ao obter pk da reunião. Retornado 0. ' + str(e))
return 0
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
reuniao_pk = self.take_reuniao_pk()
if reuniao_pk == 0:
ultima_reuniao = list(context['reuniao_list'])
if len(ultima_reuniao) > 0:
ultimo = ultima_reuniao[-1]
context['reuniao_pk'] = ultimo.pk
else:
context['reuniao_pk'] = 0
else:
context['reuniao_pk'] = reuniao_pk
context['documentoacessorio_set'] = DocumentoAcessorio.objects.filter(
reuniao__pk=context['reuniao_pk']
).order_by('id')
return context
class UpdateView(MasterDetailCrud.UpdateView):
form_class = ReuniaoForm
def get_initial(self):
return {'frente': self.object.frente}
class CreateView(MasterDetailCrud.CreateView):
form_class = ReuniaoForm
def get_initial(self):
frente = Frente.objects.get(id=self.kwargs['pk'])
return {'frente': frente}
class RemovePautaView(PermissionRequiredMixin, CreateView):
model = PautaReuniaoFrente
form_class = PautaReuniaoForm
template_name = 'parlamentares/pauta.html'
permission_required = ('parlamentares.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'].frente.pk
materias_pauta = PautaReuniaoFrente.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['numero_materias'] = len(context['materias'])
return context
def post(self, request, *args, **kwargs):
success_url = reverse('sapl.parlamentares: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):
PautaReuniaoFrente.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, FilterView):
filterset_class = PautaReuniaoFilterSet
template_name = 'parlamentares/pauta.html'
permission_required = ('parlamentares.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'].frente.pk
qr = self.request.GET.copy()
materias_pauta = PautaReuniaoFrente.objects.filter(
reuniao=context['object'])
nao_listar = [mp.materia.pk for mp in materias_pauta]
'''context['object_list'] = context['object_list'].filter(
tramitacao__unidade_tramitacao_destino__frente=context['root_pk']
).exclude(materia__pk__in=nao_listar).order_by(
"materia__tipo", "-materia__ano", "materia__numero"
)'''
context['numero_resultados'] = len(context['object_list'])
context['show_results'] = show_results_filter_set(qr)
return context
def post(self, request, *args, **kwargs):
success_url = reverse('sapl.parlamentares: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 = PautaReuniaoFrente()
pauta.reuniao = reuniao
pauta.materia = materia
pautas.append(pauta)
PautaReuniaoFrente.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__frente'
public = [RP_DETAIL, ]
ListView = None
link_return_to_parent_field = True
class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['nome', 'tipo', 'data', 'autor', 'arquivo']
class CreateView(MasterDetailCrud.CreateView):
form_class = DocumentoAcessorioCreateForm
def get_initial(self):
initial = super().get_initial()
initial['parent_pk'] = self.kwargs['pk']
return initial
class UpdateView(MasterDetailCrud.UpdateView):
layout_key = 'DocumentoAcessorioEdit'
form_class = DocumentoAcessorioEditForm
class DeleteView(MasterDetailCrud.DeleteView):
def delete(self, *args, **kwargs):
obj = self.get_object()
obj.delete()
return HttpResponseRedirect(
reverse('sapl.parlamentares:reuniao_detail',
kwargs={'pk': obj.reuniao.pk}))

3
sapl/templates/parlamentares/frente_detail.html

@ -6,4 +6,7 @@
<div class="actions btn-group btn-group-sm" role="group">
<a href="{% url 'sapl.parlamentares:frenteparlamentar_list' object.pk %}" class="btn btn-outline-primary">Composição</a>
</div>
<div class="actions btn-group btn-group-sm" role="group">
<a href="{% url 'sapl.parlamentares:reuniao_list' object.pk %}" class="btn btn-outline-primary">Reunião</a>
</div>
{% endblock actions %}

24
sapl/templates/parlamentares/layouts.yaml

@ -165,3 +165,27 @@ BlocoMembroList:
- id
- cargo:4 parlamentar
- data_entrada data_saida
Reuniao:
{% trans 'Reunião' %}:
- numero
- nome tema local_reuniao
- data hora_inicio hora_fim
- url_video url_audio
- observacao
- upload_pauta upload_ata upload_anexo
- frente
DocumentoAcessorio:
{% trans 'Documento Acessório' %}:
- nome data
- autor arquivo
- indexacao
- ementa
DocumentoAcessorioEdit:
{% trans 'Documento Acessório' %}:
- nome data
- autor arquivo
- indexacao
- ementa

33
sapl/templates/parlamentares/materias_em_tramitacao.html

@ -0,0 +1,33 @@
{% extends "crud/detail.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block actions %} {% endblock %}
{% block title %}
<h1 class="page-header">
Matérias em Tramitação <small>({{object}})</small>
</h1>
{% endblock %}
{% block detail_content %}
<fieldset>
<legend>{{ frente }}</legend>
<b>Há {{ qtde }} matéria(s) em tramitação nesta unidade.</b> <br><br>
{% for materia_em_tramitacao in page_obj %}
<b><a href="{% url 'sapl.materia:materialegislativa_detail' materia_em_tramitacao.materia.id %}">
{{ materia_em_tramitacao.materia.tipo.sigla }} {{ materia_em_tramitacao.materia.numero }}
{{ materia_em_tramitacao.materia.ano }} - {{ materia_em_tramitacao.materia.tipo }}
</b></a><br>
{{ materia_em_tramitacao.materia }}<br>
<b>Autor: </b>{{ materia_em_tramitacao.materia.autoria_set.first.autor.nome }}<br>
<b>Situação: </b>{{ materia_em_tramitacao.tramitacao.status.descricao }}<br>
<b>Data Fim Prazo da Última Tramitação: </b>
{% if materia_em_tramitacao.tramitacao.data_fim_prazo %}
{{ materia_em_tramitacao.tramitacao.data_fim_prazo }}
{% else %}
Não definida.
{% endif %}<br>
<br>
{% endfor %}
</fieldset>
{% endblock detail_content %}

113
sapl/templates/parlamentares/pauta.html

@ -0,0 +1,113 @@
{% 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 opcao %}
{% if not show_results %}
{% crispy filter.form %}
{% endif %}
{% if show_results %}
{% if numero_resultados > 0 %}
{% if numero_resultados == 1 %}
<div style="text-align: right;"><b>Pesquisa concluída com sucesso! Foi encontrada 1 matéria disponível.</b></div><br>
{% else %}
<div style="text-align: right;">
<b>Pesquisa concluída com sucesso! Foram encontradas {{ numero_resultados }} matérias disponíveis.</b>
</div>
<br>
{% endif %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
<legend>Matérias para Adicionar à Pauta</legend>
<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>
{% for materia_t in object_list %}
<tr>
<td>
<input type="checkbox" name="materia_id" value="{{ materia_t.materia.id }}" {% if check %} checked {% endif %}/>
{{ materia_t.materia.tipo.sigla }}
{{ materia_t.materia.numero }}/{{ materia_t.materia.ano }} - {{ materia_t.materia.tipo.descricao }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<input type="submit" value="Salvar" class="btn btn-primary"S>
</form>
{% else %}
<br><div style="text-align: center"><b>Nenhuma matéria disponível encontrada.</b></div>
{% endif %}
{% endif %}
{% else %}
{% if materias %}
{% if numero_materias == 1 %}
<div style="text-align: right;"><b>Há 1 matéria disponível.</b></div><br>
{% else %}
<div style="text-align: right;"><b>Há {{ numero_materias }} matérias disponíveis.</b></div><br>
{% endif %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
<legend>Matérias para Remover da Pauta</legend>
<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>
{% 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>
<input type="submit" value="Remover" class="btn btn-danger"S>
</form>
{% else %}
<br><div style="text-align: center;"><b>Não há matéria disponível.</b></div>
{% endif %}
{% 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 %}

59
sapl/templates/parlamentares/reuniao_detail.html

@ -0,0 +1,59 @@
{% extends "crud/detail_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.parlamentares.add_reuniao %}
<div class="actions btn-group float-right" role="group">
<a href="{% url 'sapl.parlamentares:pauta_add' reuniao_pk %}" class="btn btn-outline-primary">{% trans 'Adicionar Matéria(s)' %}</a>
{% if num_mats == 1 %}
<a href="{% url 'sapl.parlamentares:pauta_remove' reuniao_pk %}" class="btn btn-outline-primary btn-outline-danger">
{% trans 'Remover Matéria' %}
</a>
{% else %}
<a href="{% url 'sapl.parlamentares:pauta_remove' reuniao_pk %}" class="btn btn-outline-primary btn-outline-danger">
{% trans 'Remover Matéria(s)' %}
</a>
{% endif %}
</div>
{% endif %}
{% else %}
{% if perms.parlamentares.add_reuniao %}
<a href="{% url 'sapl.parlamentares:pauta_add' reuniao_pk %}" class="btn btn-outline-primary">{% trans 'Adicionar Matéria(s)' %}</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.parlamentares:documentoacessorio_detail' doc.pk %}">{{ doc.nome }}</a></td></tr>
{% endfor %}
</tbody>
</table>
{% if perms.parlamentares.add_reuniao %}
<a href="{% url 'sapl.parlamentares:documentoacessorio_create' reuniao_pk %}" class="btn btn-outline-primary float-right">
{% trans 'Adicionar Documento' %}
</a>
{% endif %}
{% else %}
{% if perms.parlamentares.add_reuniao %}
<a href="{% url 'sapl.parlamentares:documentoacessorio_create' reuniao_pk %}" class="btn btn-outline-primary">{% trans 'Adicionar Documento' %}</a>
{% endif %}
{% endif %}
<br /><br />
{% endblock detail_content %}
Loading…
Cancel
Save