Browse Source

Merge branch 'evita-anexada-ciclica' of https://github.com/interlegis/sapl into evita-anexada-ciclica

pull/2670/head
João 7 years ago
parent
commit
1215a88b32
  1. 5
      Dockerfile
  2. 7
      sapl/api/urls.py
  3. 126
      sapl/api/views.py
  4. 3
      sapl/comissoes/urls.py
  5. 16
      sapl/comissoes/views.py
  6. 69
      sapl/materia/forms.py
  7. 22
      sapl/materia/migrations/0044_auto_20190327_1409.py
  8. 1
      sapl/materia/models.py
  9. 13
      sapl/materia/tests/test_materia_form.py
  10. 137
      sapl/materia/views.py
  11. 19
      sapl/relatorios/views.py
  12. 22
      sapl/sessao/forms.py
  13. 6
      sapl/sessao/urls.py
  14. 39
      sapl/sessao/views.py
  15. 19
      sapl/templates/materia/relatoria_form.html
  16. 5
      sapl/templates/relatorios/relatorio_ata.html
  17. 5
      sapl/templates/sessao/expedientemateria_list.html
  18. 5
      sapl/templates/sessao/ordemdia_list.html

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

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

3
sapl/comissoes/urls.py

@ -1,7 +1,7 @@
from django.conf.urls import include, url
from sapl.comissoes.views import (CargoCrud, ComissaoCrud, ComposicaoCrud,
DocumentoAcessorioCrud, MateriasTramitacaoListView, ParticipacaoCrud,
PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud)
PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud, get_participacoes_comissao)
from .apps import AppConfig
@ -21,4 +21,5 @@ urlpatterns = [
url(r'^sistema/comissao/periodo-composicao/',
include(PeriodoComposicaoCrud.get_urls())),
url(r'^sistema/comissao/tipo/', include(TipoComissaoCrud.get_urls())),
url(r'^sistema/comissao/recupera-participacoes', get_participacoes_comissao),
]

16
sapl/comissoes/views.py

@ -2,7 +2,7 @@ import logging
from django.core.urlresolvers import reverse
from django.db.models import F
from django.http.response import HttpResponseRedirect
from django.http.response import HttpResponseRedirect, JsonResponse
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import ListView
from django.views.generic.base import RedirectView
@ -108,7 +108,7 @@ class ComposicaoCrud(MasterDetailCrud):
paginate_by = None
def take_composicao_pk(self):
username = self.request.user.username
try:
self.logger.debug('user=' + username + '. Tentando obter pk da composição.')
@ -278,3 +278,15 @@ class DocumentoAcessorioCrud(MasterDetailCrud):
return HttpResponseRedirect(
reverse('sapl.comissoes:reuniao_detail',
kwargs={'pk': obj.reuniao.pk}))
def get_participacoes_comissao(request):
parlamentares = []
composicao_id = request.GET.get('composicao_id')
if composicao_id:
parlamentares = [{'nome': p.parlamentar.nome_parlamentar, 'id': p.parlamentar.id} for p in
Participacao.objects.filter(composicao_id=composicao_id).order_by(
'parlamentar__nome_parlamentar')]
return JsonResponse(parlamentares, safe=False)

69
sapl/materia/forms.py

@ -26,7 +26,7 @@ import django_filters
import sapl
from sapl.base.models import AppConfig, Autor, TipoAutor
from sapl.comissoes.models import Comissao, Participacao
from sapl.comissoes.models import Comissao, Participacao, Composicao
from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_PUBLIC,
STATUS_TA_PRIVATE)
from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column,
@ -37,7 +37,7 @@ from sapl.materia.models import (AssuntoMateria, Autoria, MateriaAssunto,
UnidadeTramitacao)
from sapl.norma.models import (LegislacaoCitada, NormaJuridica,
TipoNormaJuridica)
from sapl.parlamentares.models import Legislatura, Partido
from sapl.parlamentares.models import Legislatura, Partido, Parlamentar
from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo
from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.utils import (YES_NO_CHOICES, SEPARADOR_HASH_PROPOSICAO,
@ -345,7 +345,7 @@ class AcompanhamentoMateriaForm(ModelForm):
self.helper = SaplFormHelper()
self.helper.layout = Layout(
Fieldset(
_('Acompanhamento de Matéria por e-mail'),
_('Acompanhamento de Matéria por e-mail'),
row1,
form_actions(label='Cadastrar')
)
@ -362,34 +362,63 @@ class DocumentoAcessorioForm(FileFieldCheckMixin, ModelForm):
class RelatoriaForm(ModelForm):
logger = logging.getLogger(__name__)
composicao = forms.ModelChoiceField(
required=True,
empty_label='---------',
queryset=Composicao.objects.all(),
label=_('Composição')
)
class Meta:
model = Relatoria
fields = ['data_designacao_relator', 'comissao', 'parlamentar',
'data_destituicao_relator', 'tipo_fim_relatoria']
fields = [
'comissao',
'data_designacao_relator',
'data_destituicao_relator',
'tipo_fim_relatoria',
'composicao',
'parlamentar'
]
widgets = {'comissao': forms.Select(attrs={'disabled': 'disabled'})}
def __init__(self, *args, **kwargs):
row1 = to_row([('comissao', 12)])
row2 = to_row([('data_designacao_relator', 4),
('data_destituicao_relator', 4),
('tipo_fim_relatoria', 4)])
row3 = to_row([('composicao', 4),
('parlamentar', 8)])
self.helper = SaplFormHelper()
self.helper.layout = SaplFormLayout(
Fieldset(_('Relatoria'), row1, row2, row3))
super().__init__(*args, **kwargs)
comissao_pk = kwargs['initial']['comissao']
composicoes = Composicao.objects.filter(comissao_id=comissao_pk)
self.fields['composicao'].choices = [('', '---------')] + \
[(c.pk, c) for c in composicoes]
def clean(self):
super(RelatoriaForm, self).clean()
self.fields['parlamentar'].choices = [('', '---------')]
if not self.is_valid():
return self.cleaned_data
def clean(self):
super().clean()
cleaned_data = self.cleaned_data
if not self.is_valid():
return cleaned_data
try:
self.logger.debug("Tentando obter objeto Comissao.")
comissao = Comissao.objects.get(id=self.initial['comissao'])
except ObjectDoesNotExist as e:
self.logger.error("Objeto Comissao não encontrado com id={} "
".A localização atual deve ser uma comissão. "
.format(self.initial['comissao']) + str(e))
self.logger.error(
"Objeto Comissao não encontrado com id={}. A localização atual deve ser uma comissão. ".format(
self.initial['comissao']) + str(e))
msg = _('A localização atual deve ser uma comissão.')
raise ValidationError(msg)
else:
@ -727,7 +756,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 +780,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)
@ -772,7 +801,7 @@ class AnexadaForm(ModelForm):
ciclico = False
anexadas_anexada = Anexada.objects.filter(materia_principal=materia_anexada)
while(anexadas_anexada and not ciclico):
while anexadas_anexada and not ciclico:
anexadas = []
for anexa in anexadas_anexada:
@ -1557,7 +1586,7 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm):
tm, am, nm = (cd.get('tipo_materia', ''),
cd.get('ano_materia', ''),
cd.get('numero_materia', ''))
if cd['numero_materia_futuro'] and \
'tipo' in cd and \
MateriaLegislativa.objects.filter(tipo=cd['tipo'].tipo_conteudo_related,
@ -1775,7 +1804,7 @@ class ConfirmarProposicaoForm(ProposicaoForm):
self._meta.fields.remove('regime_tramitacao')
# esta chamada isola o __init__ de ProposicaoForm
super(ProposicaoForm, self).__init__(*args, **kwargs)
super(ProposicaoForm, self).__init__(*args, **kwargs)
fields = [
Fieldset(
@ -1851,7 +1880,7 @@ class ConfirmarProposicaoForm(ProposicaoForm):
self.fields['tipo_readonly'].initial = self.instance.tipo.descricao
self.fields['autor_readonly'].initial = str(self.instance.autor)
if self.instance.numero_materia_futuro:
self.fields['numero_materia_futuro'].initial = self.instance.numero_materia_futuro
self.fields['numero_materia_futuro'].initial = self.instance.numero_materia_futuro
if self.instance.materia_de_vinculo:
self.fields[

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])

13
sapl/materia/tests/test_materia_form.py

@ -2,6 +2,7 @@ import pytest
from django.utils.translation import ugettext as _
from model_mommy import mommy
from sapl.comissoes.models import Comissao, TipoComissao
from sapl.materia import forms
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
@ -172,15 +173,23 @@ def test_valida_campos_obrigatorios_devolver_proposicao_form():
@pytest.mark.django_db(transaction=False)
def test_valida_campos_obrigatorios_relatoria_form():
form = forms.RelatoriaForm(data={})
tipo_comissao = mommy.make(TipoComissao)
comissao = mommy.make(Comissao,
tipo=tipo_comissao,
nome='Comissao Teste',
sigla='T',
data_criacao='2016-03-21')
form = forms.RelatoriaForm(initial={'comissao':comissao}, data={})
assert not form.is_valid()
errors = form.errors
assert errors['parlamentar'] == [_('Este campo é obrigatório.')]
assert errors['data_designacao_relator'] == [_('Este campo é obrigatório.')]
assert errors['composicao'] == [_('Este campo é obrigatório.')]
assert len(errors) == 2
assert len(errors) == 3
@pytest.mark.django_db(transaction=False)

137
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,
@ -1104,55 +1111,13 @@ class RelatoriaCrud(MasterDetailCrud):
class CreateView(MasterDetailCrud.CreateView):
form_class = RelatoriaForm
layout_key = None
logger = logging.getLogger(__name__)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
username = self.request.user.username
try:
self.logger.debug("user=" + username + ". Tentando obter objeto Comissao de pk={}.".format(
context['form'].initial['comissao']))
comissao = Comissao.objects.get(
pk=context['form'].initial['comissao'])
except:
self.logger.error("user=" + username + ". Objeto Comissão de pk={} não encontrado.".format(
context['form'].initial['comissao']))
pass
else:
self.logger.info("user=" + username + ". Objeto Comissao de pk={} obtido com sucesso.".format(
context['form'].initial['comissao']))
materia = MateriaLegislativa.objects.get(
pk=self.kwargs.get('pk'))
data_materia = materia.data_apresentacao
comissao = Comissao.objects.get(
pk=context['form'].initial['comissao'])
composicao = comissao.composicao_set.filter(
Q(periodo__data_fim__isnull=False,
periodo__data_inicio__lte=data_materia,
periodo__data_fim__gte=data_materia) |
Q(periodo__data_fim__isnull=True,
periodo__data_inicio__lte=data_materia)
)
participacoes = Participacao.objects.select_related().filter(composicao=composicao)
parlamentares = [('', '---------')] + [
(participacao.parlamentar.id, participacao.parlamentar.nome_parlamentar) for participacao in
participacoes if participacao.titular]
context['form'].fields['parlamentar'].choices = parlamentares
return context
def get_initial(self):
materia = MateriaLegislativa.objects.get(id=self.kwargs['pk'])
loc_atual = Tramitacao.objects.filter(
materia=materia).last()
loc_atual = Tramitacao.objects.filter(materia=materia).last()
if loc_atual is None:
localizacao = 0
@ -1167,47 +1132,9 @@ class RelatoriaCrud(MasterDetailCrud):
class UpdateView(MasterDetailCrud.UpdateView):
form_class = RelatoriaForm
layout_key = None
logger = logging.getLogger(__name__)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
username = self.request.user.username
try:
self.logger.debug("user=" + username + ". Tentando obter objeto Comissao de pk={}.".format(
context['form'].initial['comissao']))
comissao = Comissao.objects.get(
pk=context['form'].initial['comissao'])
except ObjectDoesNotExist:
self.logger.error("user=" + username + ". Objeto Comissão de pk={} não encontrado.".format(
context['form'].initial['comissao']))
pass
else:
self.logger.info("user=" + username + ". Objeto Comissao de pk={} obtido com sucesso.".format(
context['form'].initial['comissao']))
relatoria = Relatoria.objects.select_related(
'materia').get(pk=self.kwargs.get('pk'))
ano_materia = relatoria.materia.ano
comissao = Comissao.objects.get(
pk=context['form'].initial['comissao'])
composicoes = comissao.composicao_set.all()
composicao = comissao.composicao_set.filter(
periodo__data_inicio__year=ano_materia)
participacoes = Participacao.objects.select_related().filter(composicao=composicao)
parlamentares = [('', '---------')] + [
(participacao.parlamentar.id, participacao.parlamentar.nome_parlamentar) for participacao in
participacoes if participacao.titular]
context['form'].fields['parlamentar'].choices = parlamentares
return context
class TramitacaoCrud(MasterDetailCrud):
model = Tramitacao
@ -2035,17 +1962,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)
@ -2255,8 +2200,18 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
# issue https://github.com/interlegis/sapl/issues/1123
# TODO: usar Form
urgente = request.POST['urgente'] == 'True'
flag_error = False
for materia_id in marcadas:
flag_error = False
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"))
@ -2266,7 +2221,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,
@ -2300,7 +2255,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:

19
sapl/relatorios/views.py

@ -28,10 +28,10 @@ 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_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_explicações_pessoais, get_ocorrencias_da_sessão, get_assinaturas)
from .templates import (pdf_capa_processo_gerar,
pdf_documento_administrativo_gerar, pdf_espelho_gerar,
@ -578,6 +578,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 '&'
@ -1256,12 +1258,13 @@ def resumo_ata_pdf(request,pk):
context.update(get_materias_ordem_do_dia(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

@ -692,7 +692,29 @@ 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

@ -28,6 +28,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)
@ -75,6 +77,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/',

39
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__)
@ -620,6 +639,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
@ -1378,9 +1398,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)
@ -1486,7 +1512,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)

19
sapl/templates/materia/relatoria_form.html

@ -16,5 +16,24 @@
{% block extra_js %}
<script language="Javascript">
$("#id_comissao").attr("disabled", true);
$(document).ready(function() {
$("#id_composicao").change(function() {
var composicao_id = $("#id_composicao").val();
var json_data = { composicao_id: composicao_id };
var parlamentar_field = $("#id_parlamentar");
$.getJSON("/sistema/comissao/recupera-participacoes", json_data, function(data) {
parlamentar_field.children().remove();
parlamentar_field.append($("<option></option>").attr("value", '').text('---------'));
if (data) {
$.each(data, function(idx, parlamentar) {
parlamentar_field.append($("<option></option>").attr("value", parlamentar.id).text(parlamentar.nome));
});
}
});
});
});
</script>
{% endblock extra_js %}

5
sapl/templates/relatorios/relatorio_ata.html

@ -82,12 +82,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>

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 %}

Loading…
Cancel
Save