diff --git a/sapl/materia/migrations/0080_pautareuniaofrente.py b/sapl/materia/migrations/0080_pautareuniaofrente.py new file mode 100644 index 000000000..66ff6b7ab --- /dev/null +++ b/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',), + }, + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index 6e9a01fe4..3601e0e1b 100644 --- a/sapl/materia/models.py +++ b/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( diff --git a/sapl/parlamentares/forms.py b/sapl/parlamentares/forms.py index 31e216184..520a195fc 100755 --- a/sapl/parlamentares/forms.py +++ b/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 diff --git a/sapl/parlamentares/migrations/0039_documentoacessorio_reuniao.py b/sapl/parlamentares/migrations/0039_documentoacessorio_reuniao.py new file mode 100644 index 000000000..ce1e8ad09 --- /dev/null +++ b/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'), + }, + ), + ] diff --git a/sapl/parlamentares/models.py b/sapl/parlamentares/models.py index 807e7c835..b6d1bf2c3 100644 --- a/sapl/parlamentares/models.py +++ b/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) diff --git a/sapl/parlamentares/urls.py b/sapl/parlamentares/urls.py index 88a46c69b..8acc7ecb5 100644 --- a/sapl/parlamentares/urls.py +++ b/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\d+)/materias$', ParlamentarMateriasView.as_view(), name='parlamentar_materias'), - url(r'^parlamentar/(?P\d+)/frentes/$', get_parlamentar_frentes, name='parlamentar_frentes'), + url(r'^parlamentar/(?P\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\d+)/pauta/add', + AdicionaPautaView.as_view(), name='pauta_add'), + url(r'^sistema/frente/(?P\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\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\d+)/filiados$', + parlamentares_filiados, name='parlamentares_filiados'), url(r'^sistema/mesa-diretora/sessao-legislativa/', include(SessaoLegislativaCrud.get_urls())), @@ -106,7 +122,7 @@ urlpatterns = [ url(r'^mesa-diretora/remove-parlamentar-composicao/$', remove_parlamentar_composicao, name='remove_parlamentar_composicao'), - url(r'^parlamentar/get-sessoes-legislatura/$', + url(r'^parlamentar/get-sessoes-legislatura/$', get_sessoes_legislatura, name='get_sessoes_legislatura'), - + ] diff --git a/sapl/parlamentares/views.py b/sapl/parlamentares/views.py index 8caca7a6a..c66affdcc 100644 --- a/sapl/parlamentares/views.py +++ b/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'] = '' @@ -933,7 +945,7 @@ class MesaDiretoraView(FormView): mesa_diretora = sessao_atual.mesadiretora_set.order_by( '-data_inicio').first() if sessao_atual else None - + composicao = mesa_diretora.composicaomesa_set.all() if mesa_diretora else [] cargos_ocupados = [m.cargo for m in composicao] @@ -971,14 +983,14 @@ 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'] sessoes = SessaoLegislativa.objects.filter( legislatura=legislatura).order_by('-data_inicio') username = request.user.username - + if not sessoes: return JsonResponse({'msg': ('Nenhuma sessão encontrada!', 0)}) @@ -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) - - #TODO: quando a mesa for criada explicitamente em tabelas auxiliares, + # 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, # deve-se somente tentar recuperar a mesa, e caso nao exista # retornar o erro abaixo - # return JsonResponse({'msg': ('Nenhuma mesa encontrada na sessão!')}) + # 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( @@ -1062,11 +1077,12 @@ def insere_parlamentar_composicao(request): '%s.add_%s' % ( AppConfig.label, ComposicaoMesa._meta.model_name)): 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 @@ -1227,32 +1243,35 @@ def altera_field_mesa_public_view(request): if not sessao_selecionada: year = timezone.now().year logger.info( - f"user={username}. Tentando obter sessões com data_inicio.ano = {year}.") + f"user={username}. Tentando obter sessões com data_inicio.ano = {year}.") sessao_selecionada = sessoes.filter(data_inicio__year=year).first() if sessao_selecionada is None: logger.error(f"user={username}. Sessões não encontradas com com data_inicio.ano = {year}. " - "Selecionado o id da primeira sessão.") + "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) - - #TODO: quando a mesa for criada explicitamente em tabelas auxiliares, + # 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, # 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})) diff --git a/sapl/templates/parlamentares/frente_detail.html b/sapl/templates/parlamentares/frente_detail.html index 7f8a52f23..a32e68c74 100644 --- a/sapl/templates/parlamentares/frente_detail.html +++ b/sapl/templates/parlamentares/frente_detail.html @@ -6,4 +6,7 @@ +
+ Reunião +
{% endblock actions %} diff --git a/sapl/templates/parlamentares/layouts.yaml b/sapl/templates/parlamentares/layouts.yaml index 0064d8bfd..9b5fd80cd 100644 --- a/sapl/templates/parlamentares/layouts.yaml +++ b/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 diff --git a/sapl/templates/parlamentares/materias_em_tramitacao.html b/sapl/templates/parlamentares/materias_em_tramitacao.html new file mode 100644 index 000000000..cc2612f2b --- /dev/null +++ b/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 %} +

+ Matérias em Tramitação ({{object}}) +

+{% endblock %} + +{% block detail_content %} +
+ {{ frente }} + Há {{ qtde }} matéria(s) em tramitação nesta unidade.

+ {% for materia_em_tramitacao in page_obj %} + + {{ materia_em_tramitacao.materia.tipo.sigla }} {{ materia_em_tramitacao.materia.numero }} + {{ materia_em_tramitacao.materia.ano }} - {{ materia_em_tramitacao.materia.tipo }} +
+ {{ materia_em_tramitacao.materia }}
+ Autor: {{ materia_em_tramitacao.materia.autoria_set.first.autor.nome }}
+ Situação: {{ materia_em_tramitacao.tramitacao.status.descricao }}
+ Data Fim Prazo da Última Tramitação: + {% if materia_em_tramitacao.tramitacao.data_fim_prazo %} + {{ materia_em_tramitacao.tramitacao.data_fim_prazo }} + {% else %} + Não definida. + {% endif %}
+
+ {% endfor %} +
+{% endblock detail_content %} diff --git a/sapl/templates/parlamentares/pauta.html b/sapl/templates/parlamentares/pauta.html new file mode 100644 index 000000000..63214ff60 --- /dev/null +++ b/sapl/templates/parlamentares/pauta.html @@ -0,0 +1,113 @@ +{% extends "crud/detail.html" %} +{% load i18n crispy_forms_tags %} +{% block actions %}{% endblock %} + +{% block title %} +

+ {% if opcao %} + Adicionar Matérias à Pauta (Reunião: {{ object }}) + {% else %} + Remover Matérias da Pauta (Reunião: {{ object }}) + {% endif %} +

+{% 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 %} +
Pesquisa concluída com sucesso! Foi encontrada 1 matéria disponível.

+ {% else %} +
+ Pesquisa concluída com sucesso! Foram encontradas {{ numero_resultados }} matérias disponíveis. +
+
+ {% endif %} +
+ {% csrf_token %} +
+ Matérias para Adicionar à Pauta + +
+
+ +
+
+ + + {% for materia_t in object_list %} + + + + {% endfor %} + +
Matéria
+ + {{ materia_t.materia.tipo.sigla }} + {{ materia_t.materia.numero }}/{{ materia_t.materia.ano }} - {{ materia_t.materia.tipo.descricao }} +
+
+ +
+ {% else %} +
Nenhuma matéria disponível encontrada.
+ {% endif %} + {% endif %} + {% else %} + {% if materias %} + {% if numero_materias == 1 %} +
Há 1 matéria disponível.

+ {% else %} +
Há {{ numero_materias }} matérias disponíveis.

+ {% endif %} +
+ {% csrf_token %} +
+ Matérias para Remover da Pauta + +
+
+ +
+
+ + + {% for materia in materias %} + + + + {% endfor %} + +
Matéria
+ + {{ materia.tipo.sigla }} {{ materia.numero }}/{{ materia.ano }} - {{ materia.tipo.descricao }} +
+
+ +
+ {% else %} +
Não há matéria disponível.
+ {% endif %} + {% endif %} +{% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/sapl/templates/parlamentares/reuniao_detail.html b/sapl/templates/parlamentares/reuniao_detail.html new file mode 100644 index 000000000..6d8f60e77 --- /dev/null +++ b/sapl/templates/parlamentares/reuniao_detail.html @@ -0,0 +1,59 @@ +{% extends "crud/detail_detail.html" %} +{% load i18n %} + +{% block detail_content %} + {{ block.super }} +

Pauta

+ {% if mats %} +

Total de Registros: {{ num_mats }}

+ + + + {% for mat in mats %} + + {% endfor %} + +
Matéria
{{mat}}
+ {% if perms.parlamentares.add_reuniao %} + + {% endif %} + {% else %} + {% if perms.parlamentares.add_reuniao %} + {% trans 'Adicionar Matéria(s)' %} + {% endif %} + {% endif %} +

+

Documentos Acessórios

+ {% if docs %} +

Total de registros: {{ num_docs }}

+ + + + {% for doc in docs %} + + {% endfor %} + +
Documento Acessório
{{ doc.nome }}
+ {% if perms.parlamentares.add_reuniao %} + + {% trans 'Adicionar Documento' %} + + {% endif %} + {% else %} + {% if perms.parlamentares.add_reuniao %} + {% trans 'Adicionar Documento' %} + {% endif %} + {% endif %} +

+{% endblock detail_content %}