From 708f5c769ab414b04f7b01e8a5fff40f83b82e35 Mon Sep 17 00:00:00 2001 From: Edward Date: Fri, 22 Feb 2019 14:47:42 -0300 Subject: [PATCH] =?UTF-8?q?Verifica=C3=A7=C3=A3o=20em=20forms=20contendo?= =?UTF-8?q?=20arquivos=20(#2534)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP * WIP 2 * WIP 3 * Adiciona file check em Audiencia e Parlamentar e mostra todos os erros * adicionado o file check em mais forms * adiciona verificacao de arquivo em partido form --- sapl/audiencia/forms.py | 4 +-- sapl/base/forms.py | 61 ++++++++++++++++++++++++++++++++----- sapl/base/views.py | 9 ++++-- sapl/comissoes/forms.py | 5 +-- sapl/materia/forms.py | 10 +++--- sapl/norma/forms.py | 10 +++--- sapl/parlamentares/forms.py | 3 +- sapl/parlamentares/views.py | 13 ++++++-- sapl/protocoloadm/forms.py | 7 +++-- sapl/sessao/forms.py | 5 +-- sapl/utils.py | 34 +++++++++++++++++++++ 11 files changed, 130 insertions(+), 31 deletions(-) diff --git a/sapl/audiencia/forms.py b/sapl/audiencia/forms.py index 2912ab58d..56f4d1285 100755 --- a/sapl/audiencia/forms.py +++ b/sapl/audiencia/forms.py @@ -10,9 +10,9 @@ from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa -from sapl.utils import timezone +from sapl.utils import timezone, FileFieldCheckMixin -class AudienciaForm(forms.ModelForm): +class AudienciaForm(FileFieldCheckMixin, forms.ModelForm): logger = logging.getLogger(__name__) data_atual = timezone.now() diff --git a/sapl/base/forms.py b/sapl/base/forms.py index 25ec22949..9a8dd811e 100644 --- a/sapl/base/forms.py +++ b/sapl/base/forms.py @@ -1,4 +1,5 @@ import logging +import os from crispy_forms.bootstrap import FieldWithButtons, InlineRadios, StrictButton from sapl.crispy_layout_mixin import SaplFormHelper @@ -28,7 +29,7 @@ from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, from sapl.materia.models import ( MateriaLegislativa, UnidadeTramitacao, StatusTramitacao) from sapl.norma.models import (NormaJuridica, NormaEstatisticas) -from sapl.parlamentares.models import SessaoLegislativa +from sapl.parlamentares.models import SessaoLegislativa, Partido from sapl.sessao.models import SessaoPlenaria from sapl.settings import MAX_IMAGE_UPLOAD_SIZE from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, @@ -36,8 +37,7 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, RangeWidgetOverride, autor_label, autor_modal, models_with_gr_for_model, qs_override_django_filter, choice_anos_com_normas, choice_anos_com_materias, - FilterOverridesMetaMixin) - + FilterOverridesMetaMixin, FileFieldCheckMixin) from .models import AppConfig, CasaLegislativa @@ -198,7 +198,7 @@ class UsuarioEditForm(ModelForm): return data -class SessaoLegislativaForm(ModelForm): +class SessaoLegislativaForm(FileFieldCheckMixin, ModelForm): logger = logging.getLogger(__name__) class Meta: @@ -1108,7 +1108,7 @@ class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet): ) -class CasaLegislativaForm(ModelForm): +class CasaLegislativaForm(FileFieldCheckMixin, ModelForm): class Meta: @@ -1138,7 +1138,11 @@ class CasaLegislativaForm(ModelForm): } def clean_logotipo(self): - logotipo = self.cleaned_data.get('logotipo', False) + # chama __clean de FileFieldCheckMixin + # por estar em clean de campo + super(CasaLegislativaForm, self)._check() + + logotipo = self.cleaned_data.get('logotipo') if logotipo: if logotipo.size > MAX_IMAGE_UPLOAD_SIZE: raise ValidationError("Imagem muito grande. ( > 2MB )") @@ -1204,7 +1208,7 @@ class ConfiguracoesAppForm(ModelForm): self.logger.error('Não há casa legislativa relacionada.') raise ValidationError("Não há casa legislativa relacionada.") - if (not bool(casa.logotipo) and mostrar_brasao_painel): + if not casa.logotipo and mostrar_brasao_painel: self.logger.error('Não há logitipo configurado para esta ' 'CasaLegislativa ({}).'.format(casa)) raise ValidationError("Não há logitipo configurado para esta " @@ -1346,3 +1350,46 @@ class AlterarSenhaForm(Form): "Nova senha não pode ser igual à senha anterior") return self.cleaned_data + + +class PartidoForm(FileFieldCheckMixin, ModelForm): + + class Meta: + model = Partido + exclude = [] + + def __init__(self, *args, **kwargs): + + super(PartidoForm, self).__init__(*args, **kwargs) + + # TODO Utilizar esses campos na issue #2161 de alteração de nomes de partidos + # if self.instance: + # if self.instance.nome: + # self.fields['nome'].widget.attrs['readonly'] = True + # self.fields['sigla'].widget.attrs['readonly'] = True + + row1 = to_row( + [('sigla', 2), + ('nome', 6), + ('data_criacao', 2), + ('data_extincao', 2),]) + row2 = to_row([('observacao', 12)]) + row3 = to_row([('logo_partido', 12)]) + + self.helper = SaplFormHelper() + self.helper.layout = Layout( + row1, row2, row3, + form_actions(label='Salvar')) + + def clean(self): + + cleaned_data = super(PartidoForm, self).clean() + + if not self.is_valid(): + return cleaned_data + + if cleaned_data['data_criacao'] and cleaned_data['data_extincao']: + if cleaned_data['data_criacao'] > cleaned_data['data_extincao']: + raise ValidationError("Certifique-se de que a data de criação seja anterior à data de extinção.") + + return cleaned_data \ No newline at end of file diff --git a/sapl/base/views.py b/sapl/base/views.py index f28596ecd..e41b9e9de 100644 --- a/sapl/base/views.py +++ b/sapl/base/views.py @@ -9,7 +9,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.models import Group, User from django.contrib.auth.tokens import default_token_generator -from django.core.exceptions import ObjectDoesNotExist, PermissionDenied +from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError from django.core.mail import send_mail from django.core.urlresolvers import reverse, reverse_lazy from django.db import connection @@ -1499,8 +1499,11 @@ class AppConfigCrud(CrudAux): def gerar_hash(self, inst): inst.save() if inst.texto_original: - inst.hash_code = gerar_hash_arquivo( - inst.texto_original.path, str(inst.pk)) + try: + inst.hash_code = gerar_hash_arquivo( + inst.texto_original.path, str(inst.pk)) + except IOError: + raise ValidationError("Existem proposicoes com arquivos inexistentes.") elif inst.texto_articulado.exists(): ta = inst.texto_articulado.first() inst.hash_code = 'P' + ta.hash() + SEPARADOR_HASH_PROPOSICAO + str(inst.pk) diff --git a/sapl/comissoes/forms.py b/sapl/comissoes/forms.py index cef967102..9b0e69d72 100644 --- a/sapl/comissoes/forms.py +++ b/sapl/comissoes/forms.py @@ -12,6 +12,7 @@ from sapl.base.models import Autor, TipoAutor from sapl.comissoes.models import (Comissao, Composicao, DocumentoAcessorio, Participacao, Reuniao, Periodo) from sapl.parlamentares.models import Legislatura, Mandato, Parlamentar +from sapl.utils import FileFieldCheckMixin class ComposicaoForm(forms.ModelForm): @@ -382,7 +383,7 @@ class ReuniaoForm(ModelForm): return self.cleaned_data -class DocumentoAcessorioCreateForm(forms.ModelForm): +class DocumentoAcessorioCreateForm(FileFieldCheckMixin, forms.ModelForm): parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput()) @@ -404,7 +405,7 @@ class DocumentoAcessorioCreateForm(forms.ModelForm): reuniao = Reuniao.objects.get(id=self.initial['parent_pk']) -class DocumentoAcessorioEditForm(forms.ModelForm): +class DocumentoAcessorioEditForm(FileFieldCheckMixin, forms.ModelForm): parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput()) diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 41588f621..c8e12b0e4 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -45,7 +45,7 @@ from sapl.utils import (YES_NO_CHOICES, SEPARADOR_HASH_PROPOSICAO, MateriaPesquisaOrderingFilter, RangeWidgetOverride, autor_label, autor_modal, gerar_hash_arquivo, models_with_gr_for_model, qs_override_django_filter, - choice_anos_com_materias, FilterOverridesMetaMixin) + choice_anos_com_materias, FilterOverridesMetaMixin, FileFieldCheckMixin) from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial, DocumentoAcessorio, Numeracao, Proposicao, Relatoria, @@ -122,7 +122,7 @@ class ReceberProposicaoForm(Form): super(ReceberProposicaoForm, self).__init__(*args, **kwargs) -class MateriaSimplificadaForm(ModelForm): +class MateriaSimplificadaForm(FileFieldCheckMixin, ModelForm): logger = logging.getLogger(__name__) @@ -175,7 +175,7 @@ class MateriaSimplificadaForm(ModelForm): return cleaned_data -class MateriaLegislativaForm(ModelForm): +class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm): logger = logging.getLogger(__name__) @@ -355,7 +355,7 @@ class AcompanhamentoMateriaForm(ModelForm): super(AcompanhamentoMateriaForm, self).__init__(*args, **kwargs) -class DocumentoAcessorioForm(ModelForm): +class DocumentoAcessorioForm(FileFieldCheckMixin, ModelForm): data = forms.DateField(required=True) class Meta: @@ -1350,7 +1350,7 @@ class TipoProposicaoSelect(Select): return option -class ProposicaoForm(forms.ModelForm): +class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): logger = logging.getLogger(__name__) diff --git a/sapl/norma/forms.py b/sapl/norma/forms.py index e4423c956..5f5e0aa37 100644 --- a/sapl/norma/forms.py +++ b/sapl/norma/forms.py @@ -17,8 +17,8 @@ from sapl.crispy_layout_mixin import form_actions, to_row from sapl.materia.forms import choice_anos_com_materias from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.settings import MAX_DOC_UPLOAD_SIZE -from sapl.utils import NormaPesquisaOrderingFilter, RangeWidgetOverride,\ - choice_anos_com_normas, FilterOverridesMetaMixin +from sapl.utils import NormaPesquisaOrderingFilter, RangeWidgetOverride, \ + choice_anos_com_normas, FilterOverridesMetaMixin, FileFieldCheckMixin from .models import (AnexoNormaJuridica, AssuntoNorma, NormaJuridica, NormaRelacionada, TipoNormaJuridica, AutoriaNorma) @@ -88,7 +88,7 @@ class NormaFilterSet(django_filters.FilterSet): return queryset.filter(q) -class NormaJuridicaForm(ModelForm): +class NormaJuridicaForm(FileFieldCheckMixin, ModelForm): # Campos de MateriaLegislativa tipo_materia = forms.ModelChoiceField( @@ -200,6 +200,8 @@ class NormaJuridicaForm(ModelForm): return cleaned_data def clean_texto_integral(self): + super(NormaJuridicaForm, self).clean() + texto_integral = self.cleaned_data.get('texto_integral', False) if texto_integral and texto_integral.size > MAX_DOC_UPLOAD_SIZE: max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) @@ -269,7 +271,7 @@ class AutoriaNormaForm(ModelForm): return cd -class AnexoNormaJuridicaForm(ModelForm): +class AnexoNormaJuridicaForm(FileFieldCheckMixin, ModelForm): class Meta: model = AnexoNormaJuridica fields = ['norma', 'anexo_arquivo', 'assunto_anexo'] diff --git a/sapl/parlamentares/forms.py b/sapl/parlamentares/forms.py index 9c694d3e9..c7c8c9f9d 100755 --- a/sapl/parlamentares/forms.py +++ b/sapl/parlamentares/forms.py @@ -15,6 +15,7 @@ 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.base.models import Autor, TipoAutor from sapl.crispy_layout_mixin import form_actions, to_row @@ -196,7 +197,7 @@ class LegislaturaForm(ModelForm): return data -class ParlamentarForm(ModelForm): +class ParlamentarForm(FileFieldCheckMixin, ModelForm): class Meta: model = Parlamentar diff --git a/sapl/parlamentares/views.py b/sapl/parlamentares/views.py index 4588f9d58..3068e3779 100644 --- a/sapl/parlamentares/views.py +++ b/sapl/parlamentares/views.py @@ -19,7 +19,7 @@ from django.views.generic import FormView from django.views.generic.edit import UpdateView from image_cropping.utils import get_backend -from sapl.base.forms import SessaoLegislativaForm +from sapl.base.forms import SessaoLegislativaForm, PartidoForm from sapl.base.models import Autor from sapl.comissoes.models import Participacao from sapl.crud.base import (RP_CHANGE, RP_DETAIL, RP_LIST, Crud, CrudAux, @@ -38,7 +38,6 @@ from .models import (CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa, CargoMesaCrud = CrudAux.build(CargoMesa, 'cargo_mesa') -PartidoCrud = CrudAux.build(Partido, 'partidos') TipoDependenteCrud = CrudAux.build(TipoDependente, 'tipo_dependente') NivelInstrucaoCrud = CrudAux.build(NivelInstrucao, 'nivel_instrucao') TipoAfastamentoCrud = CrudAux.build(TipoAfastamento, 'tipo_afastamento') @@ -58,6 +57,16 @@ class SessaoLegislativaCrud(CrudAux): form_class = SessaoLegislativaForm +class PartidoCrud(CrudAux): + model = Partido + + class CreateView(CrudAux.CreateView): + form_class = PartidoForm + + class UpdateView(CrudAux.UpdateView): + form_class = PartidoForm + + class VotanteView(MasterDetailCrud): model = Votante parent_field = 'parlamentar' diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py index 6660e32cb..0e62fb877 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -23,7 +23,8 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, AnoNumeroOrderingFilter, RangeWidgetOverride, autor_label, autor_modal, choice_anos_com_protocolo, choice_force_optional, choice_anos_com_documentoadministrativo, - FilterOverridesMetaMixin, choice_anos_com_materias) + FilterOverridesMetaMixin, choice_anos_com_materias, + FileFieldCheckMixin) from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo, DocumentoAdministrativo, @@ -626,7 +627,7 @@ class ProtocoloMateriaForm(ModelForm): self.fields['data_hora_manual'].widget = forms.HiddenInput() -class DocumentoAcessorioAdministrativoForm(ModelForm): +class DocumentoAcessorioAdministrativoForm(FileFieldCheckMixin, ModelForm): class Meta: model = DocumentoAcessorioAdministrativo @@ -782,7 +783,7 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm): return self.cleaned_data -class DocumentoAdministrativoForm(ModelForm): +class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm): logger = logging.getLogger(__name__) diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index b576545d2..18df83b58 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -20,7 +20,8 @@ from sapl.materia.models import (MateriaLegislativa, StatusTramitacao, from sapl.parlamentares.models import Parlamentar, Mandato from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES, MateriaPesquisaOrderingFilter, autor_label, - autor_modal, timezone, choice_anos_com_sessaoplenaria) + autor_modal, timezone, choice_anos_com_sessaoplenaria, + FileFieldCheckMixin) from .models import (Bancada, Bloco, ExpedienteMateria, JustificativaAusencia, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria, @@ -45,7 +46,7 @@ ORDENACAO_RESUMO = [('cont_mult', 'Conteúdo Multimídia'), ('ocorr_sessao', 'Ocorrências da Sessão')] -class SessaoPlenariaForm(ModelForm): +class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm): class Meta: model = SessaoPlenaria diff --git a/sapl/utils.py b/sapl/utils.py index 16652698e..9c7350c63 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -6,6 +6,9 @@ import re from unicodedata import normalize as unicodedata_normalize import unicodedata +from django.core.files.uploadedfile import UploadedFile +from django.forms import BaseForm + from sapl.crispy_layout_mixin import SaplFormHelper from crispy_forms.layout import HTML, Button from django import forms @@ -574,6 +577,37 @@ class NormaPesquisaOrderingFilter(django_filters.OrderingFilter): return super().filter(qs, _value) +class FileFieldCheckMixin(BaseForm): + def _check(self): + cleaned_data = super(FileFieldCheckMixin, self).clean() + errors = [] + for name, campo in self.fields.items(): + if isinstance(campo, forms.fields.FileField): + error = self.errors.get(name) + if error: + msgs = [e.replace('Certifique-se de que o arquivo', + "Certifique-se de que o nome do " + "arquivo no campo '{}'".format( + campo.label)) + for e in error] + for msg in msgs: + errors.append(msg) + + arquivo = self.cleaned_data.get(name) + if arquivo and not isinstance(arquivo, UploadedFile): + if not os.path.exists(arquivo.path): + errors.append("Arquivo referenciado no campo " + " '%s' inexistente! Marque a " + "opção Limpar e Salve." % campo.label) + if errors: + raise ValidationError(errors) + return cleaned_data + + def clean(self): + """ Alias for _check() """ + return self._check() + + class AnoNumeroOrderingFilter(django_filters.OrderingFilter): choices = (('DEC', 'Ordem Decrescente'),