Browse Source

Merge branch '3.1.x' into oradores-ordem-dia

pull/2662/head
Edward 7 years ago
committed by GitHub
parent
commit
3013512931
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      Dockerfile
  2. 2
      docker-compose.yml
  3. 7
      sapl/api/urls.py
  4. 126
      sapl/api/views.py
  5. 24
      sapl/compilacao/forms.py
  6. 20
      sapl/compilacao/migrations/0011_tipotextoarticulado_rodape_global.py
  7. 7
      sapl/compilacao/models.py
  8. 6
      sapl/materia/forms.py
  9. 22
      sapl/materia/migrations/0044_auto_20190327_1409.py
  10. 1
      sapl/materia/models.py
  11. 49
      sapl/materia/views.py
  12. 21
      sapl/relatorios/views.py
  13. 22
      sapl/sessao/forms.py
  14. 6
      sapl/sessao/urls.py
  15. 118
      sapl/sessao/views.py
  16. 2
      sapl/settings.py
  17. 2
      sapl/templates/base.html
  18. 1
      sapl/templates/compilacao/layouts.yaml
  19. 3
      sapl/templates/compilacao/text_list.html
  20. 2
      sapl/templates/relatorios/header_ata.html
  21. 8
      sapl/templates/relatorios/relatorio_ata.html
  22. 2
      sapl/templates/sessao/blocos_ata/assinaturas.html
  23. 12
      sapl/templates/sessao/blocos_ata/materias_ordem_dia.html
  24. 8
      sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html
  25. 5
      sapl/templates/sessao/expedientemateria_list.html
  26. 5
      sapl/templates/sessao/ordemdia_list.html
  27. 2
      setup.py

5
Dockerfile

@ -3,7 +3,7 @@ FROM alpine:3.8
ENV BUILD_PACKAGES postgresql-dev graphviz-dev graphviz build-base git pkgconfig \
python3-dev libxml2-dev jpeg-dev libressl-dev libffi-dev libxslt-dev \
nodejs py3-lxml py3-magic postgresql-client poppler-utils antiword \
curl jq openssh-client vim openssh-client bash
curl jq openssh-client vim bash
RUN apk update --update-cache && apk upgrade
@ -32,9 +32,6 @@ RUN pip install -r /var/interlegis/sapl/requirements/dev-requirements.txt --upgr
COPY config/env_dockerfile /var/interlegis/sapl/sapl/.env
# Configura timezone para BRT
# RUN cp /usr/share/zoneinfo/America/Sao_Paulo /etc/localtime && echo "America/Sao_Paulo" > /etc/timezone
RUN python3 manage.py collectstatic --noinput --clear
# Remove .env(fake) e sapl.db da imagem

2
docker-compose.yml

@ -11,7 +11,7 @@ sapldb:
ports:
- "5432:5432"
sapl:
image: interlegis/sapl:3.1.149
image: interlegis/sapl:3.1.150
restart: always
environment:
ADMIN_PASSWORD: interlegis

7
sapl/api/urls.py

@ -8,7 +8,7 @@ from rest_framework.routers import DefaultRouter
from sapl.api.deprecated import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\
AutoresProvaveisListView, AutoresPossiveisListView, AutorListView,\
ModelChoiceView
from sapl.api.views import SaplSetViews
from sapl.api.views import SaplApiViewSetConstrutor
from .apps import AppConfig
@ -21,9 +21,10 @@ router.register(r'materia$', MateriaLegislativaViewSet)
router.register(r'sessao-plenaria', SessaoPlenariaViewSet)
for app, built_sets in SaplSetViews.items():
for app, built_sets in SaplApiViewSetConstrutor._built_sets.items():
for view_prefix, viewset in built_sets.items():
router.register(app + '/' + view_prefix, viewset)
router.register(app.label + '/' +
view_prefix._meta.model_name, viewset)
urlpatterns_router = router.urls

126
sapl/api/views.py

@ -22,8 +22,12 @@ from sapl.api.forms import SaplFilterSetMixin
from sapl.api.permissions import SaplModelPermissions
from sapl.api.serializers import ChoiceSerializer
from sapl.base.models import Autor, AppConfig, DOC_ADM_OSTENSIVO
from sapl.materia.models import Proposicao, TipoMateriaLegislativa
from sapl.materia.models import Proposicao, TipoMateriaLegislativa,\
MateriaLegislativa, Tramitacao
from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import DocumentoAdministrativo,\
DocumentoAcessorioAdministrativo, TramitacaoAdministrativo
from sapl.sessao.models import SessaoPlenaria
from sapl.utils import models_with_gr_for_model, choice_anos_com_sessaoplenaria
@ -31,20 +35,25 @@ class BusinessRulesNotImplementedMixin:
def create(self, request, *args, **kwargs):
raise Exception(_("POST Create não implementado"))
def put(self, request, *args, **kwargs):
raise Exception(_("PUT Update não implementado"))
def patch(self, request, *args, **kwargs):
raise Exception(_("PATCH Partial Update não implementado"))
def update(self, request, *args, **kwargs):
raise Exception(_("PUT and PATCH não implementado"))
def delete(self, request, *args, **kwargs):
raise Exception(_("DELETE Delete não implementado"))
class SaplApiViewSetConstrutor(ModelViewSet):
class SaplApiViewSet(ModelViewSet):
filter_backends = (DjangoFilterBackend,)
class SaplApiViewSetConstrutor():
_built_sets = {}
@classonlymethod
def get_class_for_model(cls, model):
return cls._built_sets[model._meta.app_config][model]
@classonlymethod
def build_class(cls):
import inspect
@ -95,7 +104,7 @@ class SaplApiViewSetConstrutor(ModelViewSet):
model = _model
# Define uma classe padrão ModelViewSet de DRF
class ModelSaplViewSet(cls):
class ModelSaplViewSet(SaplApiViewSet):
queryset = _model.objects.all()
# Utiliza o filtro customizado pela classe
@ -119,12 +128,12 @@ class SaplApiViewSetConstrutor(ModelViewSet):
apps_sapl = [apps.apps.get_app_config(
n[5:]) for n in settings.SAPL_APPS]
for app in apps_sapl:
built_sets[app.label] = {}
cls._built_sets[app] = {}
for model in app.get_models():
built_sets[app.label][model._meta.model_name] = build(model)
cls._built_sets[app][model] = build(model)
return built_sets
SaplApiViewSetConstrutor.build_class()
"""
1. Constroi uma rest_framework.viewsets.ModelViewSet para
@ -187,15 +196,39 @@ class SaplApiViewSetConstrutor(ModelViewSet):
}
"""
SaplSetViews = SaplApiViewSetConstrutor.build_class()
# Toda Classe construida acima, pode ser redefinida e aplicado quaisquer
# das possibilidades para uma classe normal criada a partir de
# rest_framework.viewsets.ModelViewSet conforme exemplo para a classe autor
# decorator para recuperar e transformar o default
class customize(object):
def __init__(self, model):
self.model = model
def __call__(self, cls):
class _SaplApiViewSet(
cls,
SaplApiViewSetConstrutor._built_sets[
self.model._meta.app_config][self.model]
):
pass
if hasattr(_SaplApiViewSet, 'build'):
_SaplApiViewSet = _SaplApiViewSet.build()
SaplApiViewSetConstrutor._built_sets[
self.model._meta.app_config][self.model] = _SaplApiViewSet
return _SaplApiViewSet
# Customização para AutorViewSet com implementação de actions específicas
class _AutorViewSet(SaplSetViews['base']['autor']):
@customize(Autor)
class _AutorViewSet:
"""
Neste exemplo de customização do que foi criado em
SaplApiViewSetConstrutor além do ofertado por
@ -240,7 +273,7 @@ class _AutorViewSet(SaplSetViews['base']['autor']):
return Response(serializer.data)
@classonlymethod
def build_class_with_actions(cls):
def build(cls):
models_with_gr_for_autor = models_with_gr_for_model(Autor)
@ -263,7 +296,8 @@ class _AutorViewSet(SaplSetViews['base']['autor']):
return cls
class _ParlamentarViewSet(SaplSetViews['parlamentares']['parlamentar']):
@customize(Parlamentar)
class _ParlamentarViewSet:
@action(detail=True)
def proposicoes(self, request, *args, **kwargs):
"""
@ -288,15 +322,16 @@ class _ParlamentarViewSet(SaplSetViews['parlamentares']['parlamentar']):
page = self.paginate_queryset(qs)
if page is not None:
serializer = SaplSetViews[
'materia']['proposicao'].serializer_class(page, many=True)
serializer = SaplApiViewSetConstrutor.get_class_for_model(
Proposicao).serializer_class(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(page, many=True)
return Response(serializer.data)
class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']):
@customize(Proposicao)
class _ProposicaoViewSet():
"""
list:
Retorna lista de Proposições
@ -349,7 +384,26 @@ class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']):
return qs
class _TipoMateriaLegislativaViewSet(SaplSetViews['materia']['tipomaterialegislativa']):
@customize(MateriaLegislativa)
class _MateriaLegislativaViewSet:
@action(detail=True, methods=['GET'])
def ultima_tramitacao(self, request, *args, **kwargs):
materia = self.get_object()
if not materia.tramitacao_set.exists():
return Response({})
ultima_tramitacao = materia.tramitacao_set.last()
serializer_class = SaplApiViewSetConstrutor.get_class_for_model(
Tramitacao).serializer_class(ultima_tramitacao)
return Response(serializer_class.data)
@customize(TipoMateriaLegislativa)
class _TipoMateriaLegislativaViewSet:
@action(detail=True, methods=['POST'])
def change_position(self, request, *args, **kwargs):
@ -366,7 +420,8 @@ class _TipoMateriaLegislativaViewSet(SaplSetViews['materia']['tipomaterialegisla
return Response(result)
class _DocumentoAdministrativoViewSet(SaplSetViews['protocoloadm']['documentoadministrativo']):
@customize(DocumentoAdministrativo)
class _DocumentoAdministrativoViewSet:
class DocumentoAdministrativoPermission(SaplModelPermissions):
def has_permission(self, request, view):
@ -400,8 +455,8 @@ class _DocumentoAdministrativoViewSet(SaplSetViews['protocoloadm']['documentoadm
return qs
class _DocumentoAcessorioAdministrativoViewSet(
SaplSetViews['protocoloadm']['documentoacessorioadministrativo']):
@customize(DocumentoAcessorioAdministrativo)
class _DocumentoAcessorioAdministrativoViewSet:
permission_classes = (
_DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, )
@ -414,9 +469,8 @@ class _DocumentoAcessorioAdministrativoViewSet(
return qs
class _TramitacaoAdministrativoViewSet(
SaplSetViews['protocoloadm']['tramitacaoadministrativo'],
BusinessRulesNotImplementedMixin):
@customize(TramitacaoAdministrativo)
class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin):
# TODO: Implementar regras de manutenção das tramitações de docs adms
permission_classes = (
@ -430,8 +484,8 @@ class _TramitacaoAdministrativoViewSet(
return qs
class _SessaoPlenariaViewSet(
SaplSetViews['sessao']['sessaoplenaria']):
@customize(SessaoPlenaria)
class _SessaoPlenariaViewSet:
@action(detail=False)
def years(self, request, *args, **kwargs):
@ -439,17 +493,3 @@ class _SessaoPlenariaViewSet(
serializer = ChoiceSerializer(years, many=True)
return Response(serializer.data)
SaplSetViews['base']['autor'] = _AutorViewSet.build_class_with_actions()
SaplSetViews['materia']['proposicao'] = _ProposicaoViewSet
SaplSetViews['materia']['tipomaterialegislativa'] = _TipoMateriaLegislativaViewSet
SaplSetViews['parlamentares']['parlamentar'] = _ParlamentarViewSet
SaplSetViews['protocoloadm']['documentoadministrativo'] = _DocumentoAdministrativoViewSet
SaplSetViews['protocoloadm']['documentoacessorioadministrativo'] = _DocumentoAcessorioAdministrativoViewSet
SaplSetViews['protocoloadm']['tramitacaoadministrativo'] = _TramitacaoAdministrativoViewSet
SaplSetViews['sessao']['sessaoplenaria'] = _SessaoPlenariaViewSet

24
sapl/compilacao/forms.py

@ -3,7 +3,6 @@ from datetime import timedelta
from crispy_forms.bootstrap import (Alert, FieldWithButtons, FormActions,
InlineCheckboxes, InlineRadios,
StrictButton)
from sapl.crispy_layout_mixin import SaplFormHelper
from crispy_forms.layout import (HTML, Button, Column, Div, Field, Fieldset,
Layout, Row, Submit)
from django import forms
@ -23,10 +22,12 @@ from sapl.compilacao.models import (NOTAS_PUBLICIDADE_CHOICES,
TipoTextoArticulado, TipoVide,
VeiculoPublicacao, Vide)
from sapl.compilacao.utils import DISPOSITIVO_SELECT_RELATED
from sapl.crispy_layout_mixin import SaplFormHelper
from sapl.crispy_layout_mixin import SaplFormLayout, to_column, to_row,\
form_actions
from sapl.utils import YES_NO_CHOICES
error_messages = {
'required': _('Este campo é obrigatório'),
'invalid': _('URL inválida.')
@ -59,6 +60,13 @@ class TipoTaForm(ModelForm):
widget=forms.RadioSelect(),
required=True)
rodape_global = forms.CharField(
label=TipoTextoArticulado._meta.get_field(
'rodape_global').verbose_name,
widget=forms.Textarea(attrs={'id': 'texto-rico'}),
required=False
)
class Meta:
model = TipoTextoArticulado
fields = ['sigla',
@ -66,10 +74,12 @@ class TipoTaForm(ModelForm):
'content_type',
'participacao_social',
'publicacao_func',
'perfis'
'perfis',
'rodape_global'
]
widgets = {'perfis': widgets.CheckboxSelectMultiple()}
widgets = {'perfis': widgets.CheckboxSelectMultiple(),
'rodape_global': forms.Textarea}
def __init__(self, *args, **kwargs):
@ -84,12 +94,18 @@ class TipoTaForm(ModelForm):
('perfis', 12),
])
row3 = to_row([
('rodape_global', 12),
])
self.helper = SaplFormHelper()
self.helper.layout = SaplFormLayout(
Fieldset(_('Identificação Básica'),
row1, css_class="col-md-12"),
Fieldset(_('Funcionalidades'),
row2, css_class="col-md-12"))
row2, css_class="col-md-12"),
Fieldset(_('Nota de Rodapé Global'),
row3, css_class="col-md-12"))
super(TipoTaForm, self).__init__(*args, **kwargs)

20
sapl/compilacao/migrations/0011_tipotextoarticulado_rodape_global.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-03-26 18:59
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0010_auto_20181004_1939'),
]
operations = [
migrations.AddField(
model_name='tipotextoarticulado',
name='rodape_global',
field=models.TextField(default='', help_text='A cada Tipo de Texto Articulado pode ser adicionado uma nota global de rodapé!', verbose_name='Rodapé Global'),
),
]

7
sapl/compilacao/models.py

@ -149,6 +149,13 @@ class TipoTextoArticulado(models.Model):
em edição.
"""))
rodape_global = models.TextField(
verbose_name=_('Rodapé Global'),
help_text=_('A cada Tipo de Texto Articulado pode ser adicionado '
'uma nota global de rodapé!'),
default=''
)
class Meta:
verbose_name = _('Tipo de Texto Articulado')
verbose_name_plural = _('Tipos de Texto Articulados')

6
sapl/materia/forms.py

@ -727,7 +727,7 @@ class AnexadaForm(ModelForm):
empty_label='Selecione',
)
numero = forms.CharField(label='Número', required=True)
numero = forms.IntegerField(label='Número', required=True)
ano = forms.CharField(label='Ano', required=True)
@ -751,8 +751,8 @@ class AnexadaForm(ModelForm):
ano=cleaned_data['ano'],
tipo=cleaned_data['tipo'])
except ObjectDoesNotExist:
msg = _('A MateriaLegislativa a ser anexada (numero={}, ano={}, tipo={}) não existe no cadastro'
' de matérias legislativas.'.format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo']))
msg = _('A {} {}/{} não existe no cadastro de matérias legislativas.'
.format(cleaned_data['tipo'], cleaned_data['numero'], cleaned_data['ano']))
self.logger.error("A matéria a ser anexada não existe no cadastro"
" de matérias legislativas.")
raise ValidationError(msg)

22
sapl/materia/migrations/0044_auto_20190327_1409.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-03-27 17:09
from __future__ import unicode_literals
from django.db import migrations, models
import sapl.materia.models
import sapl.utils
class Migration(migrations.Migration):
dependencies = [
('materia', '0043_auto_20190320_1749'),
]
operations = [
migrations.AlterField(
model_name='documentoacessorio',
name='arquivo',
field=models.FileField(blank=True, max_length=255, null=True, upload_to=sapl.materia.models.anexo_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Integral'),
),
]

1
sapl/materia/models.py

@ -497,6 +497,7 @@ class DocumentoAcessorio(models.Model):
arquivo = models.FileField(
blank=True,
null=True,
max_length=255,
upload_to=anexo_upload_path,
verbose_name=_('Texto Integral'),
validators=[restringe_tipos_de_arquivo_txt])

49
sapl/materia/views.py

@ -1,5 +1,11 @@
from datetime import datetime
import logging
import os
import shutil
import tempfile
import weasyprint
import itertools
from datetime import datetime
from random import choice
from string import ascii_letters, digits
@ -45,6 +51,7 @@ from sapl.materia.forms import (AnexadaForm, AutoriaForm,
from sapl.norma.models import LegislacaoCitada
from sapl.parlamentares.models import Legislatura
from sapl.protocoloadm.models import Protocolo
from sapl.settings import MEDIA_ROOT
from sapl.utils import (YES_NO_CHOICES, autor_label, autor_modal, SEPARADOR_HASH_PROPOSICAO,
gerar_hash_arquivo, get_base_url,
get_mime_type_from_file_extension, montar_row_autor,
@ -2035,17 +2042,35 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
tmp_name = os.path.join(tempfile.gettempdir(), request.FILES['arquivo'].name)
with open(tmp_name, 'wb') as destination:
for chunk in request.FILES['arquivo'].chunks():
destination.write(chunk)
doc_data = tz.localize(datetime.strptime(
request.POST['data'], "%d/%m/%Y"))
for materia_id in marcadas:
doc = DocumentoAcessorio()
doc.materia_id = materia_id
doc.tipo = tipo
doc.arquivo = request.FILES['arquivo']
doc.nome = request.POST['nome']
doc.data = tz.localize(datetime.strptime(
request.POST['data'], "%d/%m/%Y"))
doc.data = doc_data
doc.autor = request.POST['autor']
doc.ementa = request.POST['ementa']
doc.save()
diretorio = os.path.join(MEDIA_ROOT,
'sapl/public/documentoacessorio',
str(doc_data.year),
str(doc.id))
if not os.path.exists(diretorio):
os.makedirs(diretorio)
file_path = os.path.join(diretorio,
request.FILES['arquivo'].name)
shutil.copy2(tmp_name, file_path)
doc.arquivo.name = file_path.split(MEDIA_ROOT)[1] # Retira MEDIA_ROOT do nome
doc.save()
os.remove(tmp_name)
msg = _('Documento(s) criado(s).')
messages.add_message(request, messages.SUCCESS, msg)
return self.get(request, self.kwargs)
@ -2226,7 +2251,17 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
# TODO: usar Form
urgente = request.POST['urgente'] == 'True'
flag_error = False
for materia_id in marcadas:
materias_principais = [m for m in MateriaLegislativa.objects.filter(id__in=marcadas)]
materias_anexadas = [m.anexadas.all() for m in MateriaLegislativa.objects.filter(id__in=marcadas) if m.anexadas.all()]
materias_anexadas = list(itertools.chain.from_iterable(materias_anexadas))
tramitacao_local = int(request.POST['unidade_tramitacao_local'])
materias_anexadas = list(filter(lambda ma : not ma.tramitacao_set.all() or \
ma.tramitacao_set.last().unidade_tramitacao_destino.id == tramitacao_local,
materias_anexadas))
materias = set(materias_principais + materias_anexadas)
for materia in materias:
try:
data_tramitacao = tz.localize(datetime.strptime(
request.POST['data_tramitacao'], "%d/%m/%Y"))
@ -2236,7 +2271,7 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
return self.get(request, self.kwargs)
t = Tramitacao(
materia_id=materia_id,
materia=materia,
data_tramitacao=data_tramitacao,
data_encaminhamento=data_encaminhamento,
data_fim_prazo=data_fim_prazo,
@ -2270,7 +2305,7 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
status = StatusTramitacao.objects.get(id=request.POST['status'])
for materia in MateriaLegislativa.objects.filter(id__in=marcadas):
for materia in materias:
if status.indicador == 'F':
materia.em_tramitacao = False
elif self.primeira_tramitacao:

21
sapl/relatorios/views.py

@ -28,11 +28,11 @@ from sapl.settings import STATIC_ROOT
from sapl.utils import LISTA_DE_UFS, TrocaTag, filiacao_data
from sapl.sessao.views import (get_identificação_basica, get_mesa_diretora,
get_presenca_sessao,get_expedientes,
get_materias_expediente,get_oradores_expediente,
get_presenca_ordem_do_dia,get_materias_ordem_do_dia,
get_oradores_ordemdia,
get_oradores_explicações_pessoais, get_ocorrencias_da_sessão)
get_presenca_sessao,get_expedientes,
get_materias_expediente,get_oradores_expediente,
get_presenca_ordem_do_dia, get_materias_ordem_do_dia,
get_oradores_ordemdia,
get_oradores_explicações_pessoais, get_ocorrencias_da_sessão)
from .templates import (pdf_capa_processo_gerar,
pdf_documento_administrativo_gerar, pdf_espelho_gerar,
@ -579,6 +579,8 @@ def get_sessao_plenaria(sessao, casa):
# unescape HTML codes
# https://github.com/interlegis/sapl/issues/1046
conteudo = re.sub('style=".*?"', '', conteudo)
conteudo = re.sub('class=".*?"', '', conteudo)
conteudo = re.sub('<p\s+>', '<p>', conteudo)
conteudo = html.unescape(conteudo)
# escape special character '&'
@ -1290,12 +1292,13 @@ def resumo_ata_pdf(request,pk):
context.update(get_oradores_ordemdia(sessao_plenaria))
context.update(get_oradores_explicações_pessoais(sessao_plenaria))
context.update(get_ocorrencias_da_sessão(sessao_plenaria))
context.update({'object':sessao_plenaria})
context.update(get_assinaturas(sessao_plenaria))
context.update({'object': sessao_plenaria})
context.update({'data': dt.today().strftime('%d/%m/%Y')})
context.update({'rodape':rodape})
header_context = {"casa":casa, 'logotipo':casa.logotipo, 'MEDIA_URL': MEDIA_URL}
context.update({'rodape': rodape})
header_context = {"casa": casa, 'logotipo':casa.logotipo, 'MEDIA_URL': MEDIA_URL}
html_template = render_to_string('relatorios/relatorio_ata.html',context)
html_template = render_to_string('relatorios/relatorio_ata.html', context)
html_header = render_to_string('relatorios/header_ata.html', header_context)
pdf_file = make_pdf(base_url=base_url,main_template=html_template,header_template=html_header)

22
sapl/sessao/forms.py

@ -694,6 +694,28 @@ class OradorForm(ModelForm):
self.fields['parlamentar'].queryset = Parlamentar.objects.filter(
id__in=ids).order_by('nome_parlamentar')
def clean(self):
super(OradorForm, self).clean()
cleaned_data = self.cleaned_data
if not self.is_valid():
return self.cleaned_data
sessao_id = self.initial['id_sessao']
numero = self.initial.get('numero')
numero_ordem = cleaned_data['numero_ordem']
ordem = Orador.objects.filter(
sessao_plenaria_id=sessao_id,
numero_ordem=numero_ordem
).exists()
if ordem and numero_ordem != numero:
raise ValidationError(_(
"Já existe orador nesta posição de ordem de pronunciamento"
))
return self.cleaned_data
class Meta:
model = Orador
exclude = ['sessao_plenaria']

6
sapl/sessao/urls.py

@ -29,6 +29,8 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente,
remove_parlamentar_composicao,
reordernar_materias_expediente,
reordernar_materias_ordem,
renumerar_materias_ordem,
renumerar_materias_expediente,
sessao_legislativa_legislatura_ajax,
VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente,
VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView)
@ -78,6 +80,10 @@ urlpatterns = [
name="reordenar_expediente"),
url(r'^sessao/(?P<pk>\d+)/reordenar-ordem$', reordernar_materias_ordem,
name="reordenar_ordem"),
url(r'^sessao/(?P<pk>\d+)/renumerar-ordem$', renumerar_materias_ordem,
name="renumerar_ordem"),
url(r'^sessao/(?P<pk>\d+)/renumerar-materias-expediente$', renumerar_materias_expediente,
name="renumerar_materias_expediente"),
url(r'^sistema/sessao-plenaria/tipo/',
include(TipoSessaoCrud.get_urls())),
url(r'^sistema/sessao-plenaria/tipo-resultado-votacao/',

118
sapl/sessao/views.py

@ -93,6 +93,25 @@ def reordernar_materias_ordem(request, pk):
return HttpResponseRedirect(
reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk}))
def renumerar_materias_ordem(request, pk):
ordens = OrdemDia.objects.filter(sessao_plenaria_id=pk)
for ordem_num, o in enumerate(ordens, 1):
o.numero_ordem = ordem_num
o.save()
return HttpResponseRedirect(
reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk}))
def renumerar_materias_expediente(request, pk):
expedientes = ExpedienteMateria.objects.filter(sessao_plenaria_id=pk)
for exp_num, e in enumerate(expedientes, 1):
e.numero_ordem = exp_num
e.save()
return HttpResponseRedirect(
reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': pk}))
def verifica_presenca(request, model, spk):
logger = logging.getLogger(__name__)
@ -645,6 +664,7 @@ class OradorCrud(OradorCrud):
def get_initial(self):
initial = super(UpdateView, self).get_initial()
initial.update({'id_sessao': self.object.sessao_plenaria.id})
initial.update({'numero':self.object.numero_ordem})
return initial
@ -1058,9 +1078,6 @@ class ListMateriaOrdemDiaView(FormMixin, DetailView):
return self.get(self, request, args, kwargs)
def ordenar_integrantes_por_cargo(integrantes):
return sorted(integrantes, key=lambda k: k['cargo'].id)
class MesaView(FormMixin, DetailView):
template_name = 'sessao/mesa.html'
@ -1346,7 +1363,8 @@ def get_identificação_basica(sessao_plenaria):
'abertura': abertura, 'hora_inicio': sessao_plenaria.hora_inicio},
_('Encerramento: %(encerramento)s %(hora_fim)s') % {
'encerramento': encerramento, 'hora_fim': sessao_plenaria.hora_fim}
]})
],
'sessaoplenaria': sessao_plenaria})
def get_conteudo_multimidia(sessao_plenaria):
@ -1365,24 +1383,17 @@ def get_conteudo_multimidia(sessao_plenaria):
def get_mesa_diretora(sessao_plenaria):
mesa = IntegranteMesa.objects.filter(sessao_plenaria=sessao_plenaria)
integrantes = []
for m in mesa:
parlamentar = Parlamentar.objects.get(
id=m.parlamentar_id)
cargo = CargoMesa.objects.get(
id=m.cargo_id)
integrante = {'parlamentar': parlamentar, 'cargo': cargo}
integrantes.append(integrante)
return ({'mesa': ordenar_integrantes_por_cargo(integrantes)})
mesa = IntegranteMesa.objects.filter(sessao_plenaria=sessao_plenaria).order_by('cargo_id')
integrantes = [{'parlamentar': m.parlamentar,
'cargo': m.cargo} for m in mesa]
return {'mesa': integrantes}
def get_presenca_sessao(sessao_plenaria):
presencas = SessaoPlenariaPresenca.objects.filter(
sessao_plenaria_id=sessao_plenaria.id
).order_by('parlamentar__nome_parlamentar')
parlamentares_sessao = [p.parlamentar for p in presencas]
parlamentares_sessao = [p.parlamentar for p in SessaoPlenariaPresenca.objects.filter(
sessao_plenaria_id=sessao_plenaria.id
).order_by('parlamentar__nome_parlamentar')]
ausentes_sessao = JustificativaAusencia.objects.filter(
sessao_plenaria_id=sessao_plenaria.id
@ -1414,9 +1425,15 @@ def get_materias_expediente(sessao_plenaria):
ementa = m.materia.ementa
titulo = m.materia
numero = m.numero_ordem
tramitacao = m.materia.tramitacao_set.last()
turno = None
tramitacao = ''
tramitacoes = Tramitacao.objects.filter(materia=m.materia).order_by('-pk')
for aux_tramitacao in tramitacoes:
if aux_tramitacao.turno:
tramitacao = aux_tramitacao
break
turno = None
if tramitacao:
turno = get_turno(tramitacao.turno)
@ -1467,52 +1484,49 @@ def get_oradores_expediente(sessao_plenaria):
'observacao': observacao
}
oradores.append(ora)
context = {'oradores': oradores}
return context
return {'oradores': oradores}
def get_presenca_ordem_do_dia(sessao_plenaria):
mesa_aux = get_mesa_diretora(sessao_plenaria)
presencas = PresencaOrdemDia.objects.filter(
sessao_plenaria_id=sessao_plenaria.id
).order_by('parlamentar__nome_parlamentar')
parlamentares_ordem = [p.parlamentar for p in PresencaOrdemDia.objects.filter(
sessao_plenaria_id=sessao_plenaria.id
).order_by('parlamentar__nome_parlamentar')]
parlamentares_mesa_dia = [m for m in mesa_aux['mesa']]
return {'presenca_ordem': parlamentares_ordem}
presidente_dia = ''
for m in mesa_aux['mesa']:
if m['cargo'].descricao == 'Presidente':
presidente_dia = [m['parlamentar']]
break
parlamentares_ordem = [p.parlamentar for p in presencas]
def get_assinaturas(sessao_plenaria):
mesa_dia = get_mesa_diretora(sessao_plenaria)['mesa']
cont = 0
for index, parlamentar in enumerate(parlamentares_ordem):
try:
if parlamentar == parlamentares_mesa_dia[cont]["parlamentar"]:
del(parlamentares_ordem[index])
cont += 1
except IndexError:
pass
presidente_dia = [next(iter(
[m['parlamentar'] for m in mesa_dia if m['cargo'].descricao == 'Presidente']),
'')]
parlamentares_ordem = [p.parlamentar for p in PresencaOrdemDia.objects.filter(
sessao_plenaria_id=sessao_plenaria.id
).order_by('parlamentar__nome_parlamentar')]
parlamentares_mesa = [m['parlamentar'] for m in mesa_dia]
# filtra parlamentares retirando os que sao da mesa
parlamentares_ordem = [p for p in parlamentares_ordem if p not in parlamentares_mesa]
context = {}
context.update({'presenca_ordem': parlamentares_ordem})
config_assinatura_ata = AppsAppConfig.objects.first().assinatura_ata
if config_assinatura_ata == 'T' and parlamentares_ordem:
context.update(
{'texto_assinatura': 'Assinatura de Todos os Parlamentares Presentes na Sessão'})
context.update({'assinatura_mesa': parlamentares_mesa_dia,
context.update({'assinatura_mesa': mesa_dia,
'assinatura_presentes': parlamentares_ordem})
elif config_assinatura_ata == 'M' and parlamentares_mesa_dia:
elif config_assinatura_ata == 'M' and mesa_dia:
context.update(
{'texto_assinatura': 'Assinatura da Mesa Diretora da Sessão'})
context.update({'assinatura_presentes': parlamentares_mesa_dia})
context.update({'assinatura_mesa': mesa_dia})
elif config_assinatura_ata == 'P' and presidente_dia:
context.update(
{'texto_assinatura': 'Assinatura do Presidente da Sessão'})
context.update({'assinatura_presentes': presidente_dia})
context.update({'assinatura_mesa': presidente_dia})
return context
@ -1525,7 +1539,14 @@ def get_materias_ordem_do_dia(sessao_plenaria):
ementa_observacao = o.observacao
titulo = o.materia
numero = o.numero_ordem
tramitacao = o.materia.tramitacao_set.last()
tramitacao = ''
tramitacoes = Tramitacao.objects.filter(materia=o.materia).order_by('-pk')
for aux_tramitacao in tramitacoes:
if aux_tramitacao.turno:
tramitacao = aux_tramitacao
break
turno = None
if tramitacao:
turno = get_turno(tramitacao.turno)
@ -1702,6 +1723,9 @@ class ResumoView(DetailView):
# Presença Ordem do Dia
context.update(get_presenca_ordem_do_dia(self.object))
# =====================================================================
# Assinaturas
context.update(get_assinaturas(self.object))
# =====================================================================
# Matérias Ordem do Dia
# Votos de Votação Nominal de Matérias Ordem do Dia
materias_ordem_dia_votacao_nominal = OrdemDia.objects.filter(

2
sapl/settings.py

@ -41,7 +41,7 @@ ALLOWED_HOSTS = ['*']
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/?next='
SAPL_VERSION = '3.1.149'
SAPL_VERSION = '3.1.150'
if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

2
sapl/templates/base.html

@ -179,7 +179,7 @@
<small>
Desenvolvido pelo <a href="http://www.interlegis.leg.br/">Interlegis</a> em software livre e aberto.
</small>
<span>Release: 3.1.149</span>
<span>Release: 3.1.150</span>
</p>
</div>
<div class="col-md-4">

1
sapl/templates/compilacao/layouts.yaml

@ -45,3 +45,4 @@ TipoTextoArticulado:
{% trans 'Funcionalidaes' %}:
- participacao_social publicacao_func
- perfis
- rodape_global

3
sapl/templates/compilacao/text_list.html

@ -18,4 +18,7 @@
{% endblock %}
{% include 'compilacao/text_list__embedded.html'%}
{{object.tipo_ta.rodape_global|dont_break_out}}
{% endblock base_content %}

2
sapl/templates/relatorios/header_ata.html

@ -19,7 +19,7 @@
</dt>
<dd class="title">
<ul>
<li style="margin-top:10px"><h2>{{casa}}</h2></li>
<li style="margin-top:10px"><h2>{{casa.nome}}</h2></li>
<li><h3>Sistema de Apoio ao Processo Legislativo</h3></li>
</ul>
</dd>

8
sapl/templates/relatorios/relatorio_ata.html

@ -37,7 +37,7 @@
</head>
<body>
<h3 style="text-align:center;">Extrato Reunião</h3>
<h3 style="text-align:center;">Extrato Eletrônico da {{sessaoplenaria}}</h3>
{% include 'sessao/blocos_ata/identificacao_basica.html' %}
{% include 'sessao/blocos_ata/mesa_diretora.html' %}
{% include 'sessao/blocos_ata/lista_presenca.html' %}
@ -48,6 +48,7 @@
{% include 'sessao/blocos_ata/materias_ordem_dia.html' %}
{% include 'sessao/blocos_ata/oradores_ordemdia.html' %}
{% include 'sessao/blocos_ata/oradores_explicacoes.html' %}
{% include 'sessao/blocos_ata/ocorrencias_da_sessao.html' %}
{% if assinatura_mesa or assinatura_presentes %}
@ -82,12 +83,13 @@
<tr style="margin-top:20px">
<td>
<div style="float: left; position: relative;top: -50px; left: 8px; width: 120px;">_____________________</br>
<p style="font-size:8pt"><b>{{p.cargo}}: </b> {{p.parlamentar.nome_completo}} / {{ p.parlamentar|filiacao_data_filter:object.data_inicio }}</p>
<p style="font-size:8pt">
{{p.nome_completo}} / {{ p|filiacao_data_filter:object.data_inicio }}</p>
</br></br></br>
</div>
{% else %}
<div style="float: left; position: relative; top: -50px;left: 142px; width: 120px; margin-right:-220px;">_____________________ </br>
<p style="font-size:8pt">{{p.parlamentar.nome_completo}} / {{ p.parlamentar|filiacao_data_filter:object.data_inicio }}</p>
<p style="font-size:8pt">{{p.nome_completo}} / {{ p|filiacao_data_filter:object.data_inicio }}</p>
</br></br></br>
</div>
</td>

2
sapl/templates/sessao/blocos_ata/assinaturas.html

@ -11,7 +11,7 @@
{% endfor %}
{% for p in assinatura_presentes %}
<div class="col-md-6">___________________________________________ </br>
{{p.parlamentar.nome_completo}} / {{ p.parlamentar|filiacao_data_filter:object.data_inicio }}
{{p.nome_completo}} / {{ p|filiacao_data_filter:object.data_inicio }}
</br></br></br>
</div>
{% endfor %}

12
sapl/templates/sessao/blocos_ata/materias_ordem_dia.html

@ -1,19 +1,19 @@
<fieldset>
</br>
<p align="justify">
<strong>Matérias da Ordem do Dia: </strong>
</br></br>
{% for m in materias_ordem %}
<p>
<b>Materia do Expediente: {{m.numero}} - {{m.titulo}}</b>
<b>{{m.numero}} - {{m.titulo}}</b>
Descrição: {{m.ementa|safe}}
Autor: {{ m.autor|length|pluralize:"es" }}: {{ m.autor|join:', ' }}
Autor{{ m.autor|length|pluralize:"es" }}: {{ m.autor|join:', ' }}
{% if m.numero_protocolo %}
Número de Protocolo: {{ m.numero_protocolo }}
{% endif %}
{% if m.numero_processo %}
Processo:{{ m.numero_processo }}
{% endif %}
{%if m.turno %}
Turno: {{m.turno}}
{%endif %}
Tipo: {{m.tipo_votacao}}
Sim:{{m.voto_sim}}
Não:{{m.voto_nao}}
@ -25,6 +25,6 @@
/ {{voto.0}} - {{voto.1}}
{% endfor %};
{% endif %}
</p>
{% endfor %}
</p>
</fieldset>

8
sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html

@ -1,8 +1,8 @@
<fieldset>
<p align="justify">
{% if object.ocorrenciasessao.conteudo %}
<strong>Ocorrências da Sessão: </strong>
{{object.ocorrenciasessao.conteudo|striptags|safe}}
<p align="justify">
{% if object.ocorrenciasessao.conteudo %}
<strong>Ocorrências da Sessão: </strong>
{{object.ocorrenciasessao.conteudo|striptags|safe}}
{% endif %}
</p>
</fieldset>

5
sapl/templates/sessao/expedientemateria_list.html

@ -7,7 +7,10 @@
{% if perms|get_add_perm:view %}
<a href="{% url 'sapl.sessao:reordenar_expediente' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Ajustar Ordenação {% endblocktrans %}
{% blocktrans with verbose_name=view.verbose_name %} Reordenar pela precedência {% endblocktrans %}
</a>
<a href="{% url 'sapl.sessao:renumerar_materias_expediente' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Renumerar Expediente {% endblocktrans %}
</a>
<a href="{% url 'sapl.sessao:adicionar_varias_materias_expediente' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %}

5
sapl/templates/sessao/ordemdia_list.html

@ -7,7 +7,10 @@
{% if perms|get_add_perm:view %}
<a href="{% url 'sapl.sessao:reordenar_ordem' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Ajustar Ordenação {% endblocktrans %}
{% blocktrans with verbose_name=view.verbose_name %} Reordenar pela precedência {% endblocktrans %}
</a>
<a href="{% url 'sapl.sessao:renumerar_ordem' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Renumerar Ordem {% endblocktrans %}
</a>
<a href="{% url 'sapl.sessao:adicionar_varias_materias_ordem_dia' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %}

2
setup.py

@ -43,7 +43,7 @@ install_requires = [
]
setup(
name='interlegis-sapl',
version='3.1.149',
version='3.1.150',
packages=find_packages(),
include_package_data=True,
license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007',

Loading…
Cancel
Save