Browse Source

Verificação em forms contendo arquivos (#2534)

* 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
pull/2460/head
Edward 6 years ago
committed by GitHub
parent
commit
708f5c769a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      sapl/audiencia/forms.py
  2. 61
      sapl/base/forms.py
  3. 9
      sapl/base/views.py
  4. 5
      sapl/comissoes/forms.py
  5. 10
      sapl/materia/forms.py
  6. 10
      sapl/norma/forms.py
  7. 3
      sapl/parlamentares/forms.py
  8. 13
      sapl/parlamentares/views.py
  9. 7
      sapl/protocoloadm/forms.py
  10. 5
      sapl/sessao/forms.py
  11. 34
      sapl/utils.py

4
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 SaplFormHelper
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa 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__) logger = logging.getLogger(__name__)
data_atual = timezone.now() data_atual = timezone.now()

61
sapl/base/forms.py

@ -1,4 +1,5 @@
import logging import logging
import os
from crispy_forms.bootstrap import FieldWithButtons, InlineRadios, StrictButton from crispy_forms.bootstrap import FieldWithButtons, InlineRadios, StrictButton
from sapl.crispy_layout_mixin import SaplFormHelper 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 ( from sapl.materia.models import (
MateriaLegislativa, UnidadeTramitacao, StatusTramitacao) MateriaLegislativa, UnidadeTramitacao, StatusTramitacao)
from sapl.norma.models import (NormaJuridica, NormaEstatisticas) 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.sessao.models import SessaoPlenaria
from sapl.settings import MAX_IMAGE_UPLOAD_SIZE from sapl.settings import MAX_IMAGE_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, 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, RangeWidgetOverride, autor_label, autor_modal,
models_with_gr_for_model, qs_override_django_filter, models_with_gr_for_model, qs_override_django_filter,
choice_anos_com_normas, choice_anos_com_materias, choice_anos_com_normas, choice_anos_com_materias,
FilterOverridesMetaMixin) FilterOverridesMetaMixin, FileFieldCheckMixin)
from .models import AppConfig, CasaLegislativa from .models import AppConfig, CasaLegislativa
@ -198,7 +198,7 @@ class UsuarioEditForm(ModelForm):
return data return data
class SessaoLegislativaForm(ModelForm): class SessaoLegislativaForm(FileFieldCheckMixin, ModelForm):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Meta: class Meta:
@ -1108,7 +1108,7 @@ class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet):
) )
class CasaLegislativaForm(ModelForm): class CasaLegislativaForm(FileFieldCheckMixin, ModelForm):
class Meta: class Meta:
@ -1138,7 +1138,11 @@ class CasaLegislativaForm(ModelForm):
} }
def clean_logotipo(self): 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:
if logotipo.size > MAX_IMAGE_UPLOAD_SIZE: if logotipo.size > MAX_IMAGE_UPLOAD_SIZE:
raise ValidationError("Imagem muito grande. ( > 2MB )") raise ValidationError("Imagem muito grande. ( > 2MB )")
@ -1204,7 +1208,7 @@ class ConfiguracoesAppForm(ModelForm):
self.logger.error('Não há casa legislativa relacionada.') self.logger.error('Não há casa legislativa relacionada.')
raise ValidationError("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 ' self.logger.error('Não há logitipo configurado para esta '
'CasaLegislativa ({}).'.format(casa)) 'CasaLegislativa ({}).'.format(casa))
raise ValidationError("Não há logitipo configurado para esta " 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") "Nova senha não pode ser igual à senha anterior")
return self.cleaned_data 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

9
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.mixins import PermissionRequiredMixin
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django.contrib.auth.tokens import default_token_generator 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.mail import send_mail
from django.core.urlresolvers import reverse, reverse_lazy from django.core.urlresolvers import reverse, reverse_lazy
from django.db import connection from django.db import connection
@ -1499,8 +1499,11 @@ class AppConfigCrud(CrudAux):
def gerar_hash(self, inst): def gerar_hash(self, inst):
inst.save() inst.save()
if inst.texto_original: if inst.texto_original:
inst.hash_code = gerar_hash_arquivo( try:
inst.texto_original.path, str(inst.pk)) 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(): elif inst.texto_articulado.exists():
ta = inst.texto_articulado.first() ta = inst.texto_articulado.first()
inst.hash_code = 'P' + ta.hash() + SEPARADOR_HASH_PROPOSICAO + str(inst.pk) inst.hash_code = 'P' + ta.hash() + SEPARADOR_HASH_PROPOSICAO + str(inst.pk)

5
sapl/comissoes/forms.py

@ -12,6 +12,7 @@ from sapl.base.models import Autor, TipoAutor
from sapl.comissoes.models import (Comissao, Composicao, DocumentoAcessorio, from sapl.comissoes.models import (Comissao, Composicao, DocumentoAcessorio,
Participacao, Reuniao, Periodo) Participacao, Reuniao, Periodo)
from sapl.parlamentares.models import Legislatura, Mandato, Parlamentar from sapl.parlamentares.models import Legislatura, Mandato, Parlamentar
from sapl.utils import FileFieldCheckMixin
class ComposicaoForm(forms.ModelForm): class ComposicaoForm(forms.ModelForm):
@ -382,7 +383,7 @@ class ReuniaoForm(ModelForm):
return self.cleaned_data return self.cleaned_data
class DocumentoAcessorioCreateForm(forms.ModelForm): class DocumentoAcessorioCreateForm(FileFieldCheckMixin, forms.ModelForm):
parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput()) 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']) 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()) parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput())

10
sapl/materia/forms.py

@ -45,7 +45,7 @@ from sapl.utils import (YES_NO_CHOICES, SEPARADOR_HASH_PROPOSICAO,
MateriaPesquisaOrderingFilter, RangeWidgetOverride, MateriaPesquisaOrderingFilter, RangeWidgetOverride,
autor_label, autor_modal, gerar_hash_arquivo, autor_label, autor_modal, gerar_hash_arquivo,
models_with_gr_for_model, qs_override_django_filter, 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, from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial,
DocumentoAcessorio, Numeracao, Proposicao, Relatoria, DocumentoAcessorio, Numeracao, Proposicao, Relatoria,
@ -122,7 +122,7 @@ class ReceberProposicaoForm(Form):
super(ReceberProposicaoForm, self).__init__(*args, **kwargs) super(ReceberProposicaoForm, self).__init__(*args, **kwargs)
class MateriaSimplificadaForm(ModelForm): class MateriaSimplificadaForm(FileFieldCheckMixin, ModelForm):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -175,7 +175,7 @@ class MateriaSimplificadaForm(ModelForm):
return cleaned_data return cleaned_data
class MateriaLegislativaForm(ModelForm): class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -355,7 +355,7 @@ class AcompanhamentoMateriaForm(ModelForm):
super(AcompanhamentoMateriaForm, self).__init__(*args, **kwargs) super(AcompanhamentoMateriaForm, self).__init__(*args, **kwargs)
class DocumentoAcessorioForm(ModelForm): class DocumentoAcessorioForm(FileFieldCheckMixin, ModelForm):
data = forms.DateField(required=True) data = forms.DateField(required=True)
class Meta: class Meta:
@ -1350,7 +1350,7 @@ class TipoProposicaoSelect(Select):
return option return option
class ProposicaoForm(forms.ModelForm): class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

10
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.forms import choice_anos_com_materias
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.settings import MAX_DOC_UPLOAD_SIZE from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.utils import NormaPesquisaOrderingFilter, RangeWidgetOverride,\ from sapl.utils import NormaPesquisaOrderingFilter, RangeWidgetOverride, \
choice_anos_com_normas, FilterOverridesMetaMixin choice_anos_com_normas, FilterOverridesMetaMixin, FileFieldCheckMixin
from .models import (AnexoNormaJuridica, AssuntoNorma, NormaJuridica, NormaRelacionada, from .models import (AnexoNormaJuridica, AssuntoNorma, NormaJuridica, NormaRelacionada,
TipoNormaJuridica, AutoriaNorma) TipoNormaJuridica, AutoriaNorma)
@ -88,7 +88,7 @@ class NormaFilterSet(django_filters.FilterSet):
return queryset.filter(q) return queryset.filter(q)
class NormaJuridicaForm(ModelForm): class NormaJuridicaForm(FileFieldCheckMixin, ModelForm):
# Campos de MateriaLegislativa # Campos de MateriaLegislativa
tipo_materia = forms.ModelChoiceField( tipo_materia = forms.ModelChoiceField(
@ -200,6 +200,8 @@ class NormaJuridicaForm(ModelForm):
return cleaned_data return cleaned_data
def clean_texto_integral(self): def clean_texto_integral(self):
super(NormaJuridicaForm, self).clean()
texto_integral = self.cleaned_data.get('texto_integral', False) texto_integral = self.cleaned_data.get('texto_integral', False)
if texto_integral and texto_integral.size > MAX_DOC_UPLOAD_SIZE: if texto_integral and texto_integral.size > MAX_DOC_UPLOAD_SIZE:
max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024))
@ -269,7 +271,7 @@ class AutoriaNormaForm(ModelForm):
return cd return cd
class AnexoNormaJuridicaForm(ModelForm): class AnexoNormaJuridicaForm(FileFieldCheckMixin, ModelForm):
class Meta: class Meta:
model = AnexoNormaJuridica model = AnexoNormaJuridica
fields = ['norma', 'anexo_arquivo', 'assunto_anexo'] fields = ['norma', 'anexo_arquivo', 'assunto_anexo']

3
sapl/parlamentares/forms.py

@ -15,6 +15,7 @@ from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from floppyforms.widgets import ClearableFileInput from floppyforms.widgets import ClearableFileInput
from image_cropping.widgets import CropWidget, ImageCropWidget from image_cropping.widgets import CropWidget, ImageCropWidget
from sapl.utils import FileFieldCheckMixin
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.crispy_layout_mixin import form_actions, to_row from sapl.crispy_layout_mixin import form_actions, to_row
@ -196,7 +197,7 @@ class LegislaturaForm(ModelForm):
return data return data
class ParlamentarForm(ModelForm): class ParlamentarForm(FileFieldCheckMixin, ModelForm):
class Meta: class Meta:
model = Parlamentar model = Parlamentar

13
sapl/parlamentares/views.py

@ -19,7 +19,7 @@ from django.views.generic import FormView
from django.views.generic.edit import UpdateView from django.views.generic.edit import UpdateView
from image_cropping.utils import get_backend 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.base.models import Autor
from sapl.comissoes.models import Participacao from sapl.comissoes.models import Participacao
from sapl.crud.base import (RP_CHANGE, RP_DETAIL, RP_LIST, Crud, CrudAux, 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') CargoMesaCrud = CrudAux.build(CargoMesa, 'cargo_mesa')
PartidoCrud = CrudAux.build(Partido, 'partidos')
TipoDependenteCrud = CrudAux.build(TipoDependente, 'tipo_dependente') TipoDependenteCrud = CrudAux.build(TipoDependente, 'tipo_dependente')
NivelInstrucaoCrud = CrudAux.build(NivelInstrucao, 'nivel_instrucao') NivelInstrucaoCrud = CrudAux.build(NivelInstrucao, 'nivel_instrucao')
TipoAfastamentoCrud = CrudAux.build(TipoAfastamento, 'tipo_afastamento') TipoAfastamentoCrud = CrudAux.build(TipoAfastamento, 'tipo_afastamento')
@ -58,6 +57,16 @@ class SessaoLegislativaCrud(CrudAux):
form_class = SessaoLegislativaForm 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): class VotanteView(MasterDetailCrud):
model = Votante model = Votante
parent_field = 'parlamentar' parent_field = 'parlamentar'

7
sapl/protocoloadm/forms.py

@ -23,7 +23,8 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, AnoNumeroOrderingFilter,
RangeWidgetOverride, autor_label, autor_modal, RangeWidgetOverride, autor_label, autor_modal,
choice_anos_com_protocolo, choice_force_optional, choice_anos_com_protocolo, choice_force_optional,
choice_anos_com_documentoadministrativo, choice_anos_com_documentoadministrativo,
FilterOverridesMetaMixin, choice_anos_com_materias) FilterOverridesMetaMixin, choice_anos_com_materias,
FileFieldCheckMixin)
from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo, from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo,
DocumentoAdministrativo, DocumentoAdministrativo,
@ -626,7 +627,7 @@ class ProtocoloMateriaForm(ModelForm):
self.fields['data_hora_manual'].widget = forms.HiddenInput() self.fields['data_hora_manual'].widget = forms.HiddenInput()
class DocumentoAcessorioAdministrativoForm(ModelForm): class DocumentoAcessorioAdministrativoForm(FileFieldCheckMixin, ModelForm):
class Meta: class Meta:
model = DocumentoAcessorioAdministrativo model = DocumentoAcessorioAdministrativo
@ -782,7 +783,7 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm):
return self.cleaned_data return self.cleaned_data
class DocumentoAdministrativoForm(ModelForm): class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

5
sapl/sessao/forms.py

@ -20,7 +20,8 @@ from sapl.materia.models import (MateriaLegislativa, StatusTramitacao,
from sapl.parlamentares.models import Parlamentar, Mandato from sapl.parlamentares.models import Parlamentar, Mandato
from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES, from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES,
MateriaPesquisaOrderingFilter, autor_label, 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, from .models import (Bancada, Bloco, ExpedienteMateria, JustificativaAusencia,
Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria,
@ -45,7 +46,7 @@ ORDENACAO_RESUMO = [('cont_mult', 'Conteúdo Multimídia'),
('ocorr_sessao', 'Ocorrências da Sessão')] ('ocorr_sessao', 'Ocorrências da Sessão')]
class SessaoPlenariaForm(ModelForm): class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm):
class Meta: class Meta:
model = SessaoPlenaria model = SessaoPlenaria

34
sapl/utils.py

@ -6,6 +6,9 @@ import re
from unicodedata import normalize as unicodedata_normalize from unicodedata import normalize as unicodedata_normalize
import unicodedata import unicodedata
from django.core.files.uploadedfile import UploadedFile
from django.forms import BaseForm
from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormHelper
from crispy_forms.layout import HTML, Button from crispy_forms.layout import HTML, Button
from django import forms from django import forms
@ -574,6 +577,37 @@ class NormaPesquisaOrderingFilter(django_filters.OrderingFilter):
return super().filter(qs, _value) 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): class AnoNumeroOrderingFilter(django_filters.OrderingFilter):
choices = (('DEC', 'Ordem Decrescente'), choices = (('DEC', 'Ordem Decrescente'),

Loading…
Cancel
Save