diff --git a/.gitignore b/.gitignore index 429873229..cb6cce48e 100644 --- a/.gitignore +++ b/.gitignore @@ -87,7 +87,7 @@ target/ *.sublime-workspace .ipynb_checkpoints/ *.ipynb - +.vscode/* # specific to this project whoosh_index diff --git a/Dockerfile b/Dockerfile index d1a9ca29f..3f3adc78e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -FROM alpine: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 vim openssh-client + python3-dev libxml2-dev jpeg-dev libressl-dev libffi-dev libxslt-dev \ + nodejs npm py3-lxml py3-magic postgresql-client poppler-utils antiword vim openssh-client RUN apk update --update-cache && apk upgrade @@ -40,7 +40,7 @@ COPY config/env_dockerfile /var/interlegis/sapl/sapl/.env # compilescss - Precompile all occurrences of your SASS/SCSS files for the whole project into css files -RUN python3 manage.py bower_install -- --allow-root --no-input && \ +RUN python3 manage.py bower_install --allow-root && \ python3 manage.py compilescss RUN python3 manage.py collectstatic --noinput --clear diff --git a/README.rst b/README.rst index a54f4e9f2..dd21aecf0 100644 --- a/README.rst +++ b/README.rst @@ -53,12 +53,6 @@ Orientações gerais sobre o GitHub - -Logo dos Partidos -=================================== - `Logo dos Partidos `_ - - Perguntas Frequentes =================================== `Perguntas Frequentes `_ diff --git a/docker-compose.yml b/docker-compose.yml index b9d6d10ed..c911a5280 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ sapldb: - image: postgres:9.6.8-alpine + image: postgres:10.5-alpine restart: always environment: POSTGRES_PASSWORD: sapl @@ -11,7 +11,7 @@ sapldb: ports: - "5432:5432" sapl: - image: interlegis/sapl:3.1.124 + image: interlegis/sapl:3.1.131 restart: always environment: ADMIN_PASSWORD: interlegis diff --git a/docs/logo_partidos.rst b/docs/logo_partidos.rst deleted file mode 100644 index cfbe361a5..000000000 --- a/docs/logo_partidos.rst +++ /dev/null @@ -1,7 +0,0 @@ - -======================== -Logo Partidos Políticos -======================== - -Dentro da pasta docs/logo_partidos existem os logotipos dos Partidos Políticos em formato png, quem quiser pode usá-los no cadastro de partidos em tabelas auxiliares. - diff --git a/docs/logo_partidos/dem.png b/docs/logo_partidos/dem.png deleted file mode 100644 index c532da9a8..000000000 Binary files a/docs/logo_partidos/dem.png and /dev/null differ diff --git a/docs/logo_partidos/pdt.png b/docs/logo_partidos/pdt.png deleted file mode 100644 index 1ff2f7833..000000000 Binary files a/docs/logo_partidos/pdt.png and /dev/null differ diff --git a/docs/logo_partidos/pfl.png b/docs/logo_partidos/pfl.png deleted file mode 100644 index 9144fff82..000000000 Binary files a/docs/logo_partidos/pfl.png and /dev/null differ diff --git a/docs/logo_partidos/phs.png b/docs/logo_partidos/phs.png deleted file mode 100644 index 84600c74f..000000000 Binary files a/docs/logo_partidos/phs.png and /dev/null differ diff --git a/docs/logo_partidos/pl.png b/docs/logo_partidos/pl.png deleted file mode 100644 index 44b794e4e..000000000 Binary files a/docs/logo_partidos/pl.png and /dev/null differ diff --git a/docs/logo_partidos/pmdb.png b/docs/logo_partidos/pmdb.png deleted file mode 100644 index 26d8370e3..000000000 Binary files a/docs/logo_partidos/pmdb.png and /dev/null differ diff --git a/docs/logo_partidos/pmn.png b/docs/logo_partidos/pmn.png deleted file mode 100644 index 808e6618c..000000000 Binary files a/docs/logo_partidos/pmn.png and /dev/null differ diff --git a/docs/logo_partidos/pp.png b/docs/logo_partidos/pp.png deleted file mode 100644 index 2de6ead1a..000000000 Binary files a/docs/logo_partidos/pp.png and /dev/null differ diff --git a/docs/logo_partidos/pps.png b/docs/logo_partidos/pps.png deleted file mode 100644 index 180a11564..000000000 Binary files a/docs/logo_partidos/pps.png and /dev/null differ diff --git a/docs/logo_partidos/prb.png b/docs/logo_partidos/prb.png deleted file mode 100644 index d4ba55665..000000000 Binary files a/docs/logo_partidos/prb.png and /dev/null differ diff --git a/docs/logo_partidos/prona.png b/docs/logo_partidos/prona.png deleted file mode 100644 index 0e5f28a1e..000000000 Binary files a/docs/logo_partidos/prona.png and /dev/null differ diff --git a/docs/logo_partidos/prp.png b/docs/logo_partidos/prp.png deleted file mode 100644 index 41e0e1eb0..000000000 Binary files a/docs/logo_partidos/prp.png and /dev/null differ diff --git a/docs/logo_partidos/prtb.png b/docs/logo_partidos/prtb.png deleted file mode 100644 index 0e0a0fa99..000000000 Binary files a/docs/logo_partidos/prtb.png and /dev/null differ diff --git a/docs/logo_partidos/psb.png b/docs/logo_partidos/psb.png deleted file mode 100644 index 76222505b..000000000 Binary files a/docs/logo_partidos/psb.png and /dev/null differ diff --git a/docs/logo_partidos/psc.png b/docs/logo_partidos/psc.png deleted file mode 100644 index cf0716c12..000000000 Binary files a/docs/logo_partidos/psc.png and /dev/null differ diff --git a/docs/logo_partidos/psdb.png b/docs/logo_partidos/psdb.png deleted file mode 100644 index 3cd38d7e4..000000000 Binary files a/docs/logo_partidos/psdb.png and /dev/null differ diff --git a/docs/logo_partidos/psdc.png b/docs/logo_partidos/psdc.png deleted file mode 100644 index e218c594e..000000000 Binary files a/docs/logo_partidos/psdc.png and /dev/null differ diff --git a/docs/logo_partidos/psl.png b/docs/logo_partidos/psl.png deleted file mode 100644 index 75a40fa22..000000000 Binary files a/docs/logo_partidos/psl.png and /dev/null differ diff --git a/docs/logo_partidos/psol.png b/docs/logo_partidos/psol.png deleted file mode 100644 index d508400d9..000000000 Binary files a/docs/logo_partidos/psol.png and /dev/null differ diff --git a/docs/logo_partidos/pt.png b/docs/logo_partidos/pt.png deleted file mode 100644 index f552f1fc9..000000000 Binary files a/docs/logo_partidos/pt.png and /dev/null differ diff --git a/docs/logo_partidos/ptb.png b/docs/logo_partidos/ptb.png deleted file mode 100644 index d963f94f6..000000000 Binary files a/docs/logo_partidos/ptb.png and /dev/null differ diff --git a/docs/logo_partidos/ptc.png b/docs/logo_partidos/ptc.png deleted file mode 100644 index 7d639a9f1..000000000 Binary files a/docs/logo_partidos/ptc.png and /dev/null differ diff --git a/docs/logo_partidos/ptdob.png b/docs/logo_partidos/ptdob.png deleted file mode 100644 index e557521fd..000000000 Binary files a/docs/logo_partidos/ptdob.png and /dev/null differ diff --git a/docs/logo_partidos/ptn.png b/docs/logo_partidos/ptn.png deleted file mode 100644 index 7b4c01296..000000000 Binary files a/docs/logo_partidos/ptn.png and /dev/null differ diff --git a/docs/logo_partidos/pv.png b/docs/logo_partidos/pv.png deleted file mode 100644 index 766023320..000000000 Binary files a/docs/logo_partidos/pv.png and /dev/null differ diff --git a/release.sh b/release.sh index 958e91b21..d8301aafe 100755 --- a/release.sh +++ b/release.sh @@ -21,7 +21,7 @@ function bump_version { function commit_and_push { echo "committing..." - git add docker-compose.yml setup.py + git add docker-compose.yml setup.py sapl/templates/base.html git commit -m "Release: $NEXT_VERSION" git tag $NEXT_VERSION diff --git a/requirements/dev-requirements.txt b/requirements/dev-requirements.txt index 49ded787e..dba8788b2 100644 --- a/requirements/dev-requirements.txt +++ b/requirements/dev-requirements.txt @@ -2,7 +2,7 @@ autopep8==1.2.4 beautifulsoup4==4.6.0 -django-debug-toolbar==1.5 +django-debug-toolbar==1.8 ipdb==0.10.1 pdbpp==0.9.2 pip-review==0.4 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 6d9d79d66..556c5d112 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,25 +1,22 @@ dj-database-url==0.4.1 django-haystack==2.6.0 -django>=1.9,<1.10 -# TODO O django-admin-bootstrapped 2.5.7 não inseriu a mudança que permite -# a compatibilidade com Django 1.9+. A linha abaixo será mudada quando uma -# nova versão do django-admin-bootstrapped for lançada -git+git://github.com/django-admin-bootstrapped/django-admin-bootstrapped.git +django>=1.10,<1.11 +git+git://github.com/rubgombar1/django-admin-bootstrapped.git django-bootstrap3==7.0.1 -django-bower==5.1.0 +django-bower==5.2.0 django-braces==1.9.0 django-compressor==2.0 -django-crispy-forms==1.6.0 -django-extensions==1.6.7 -django-extra-views==0.8.0 +django-crispy-forms==1.6.1 +django-extensions==1.9.8 +django-extra-views==0.11.0 django-filter==0.15.3 django-floppyforms==1.6.2 -django-model-utils==2.5 -django-sass-processor==0.5.4 +django-model-utils==3.1.1 +django-sass-processor==0.5.8 djangorestframework==3.4.0 -drfdocs==0.0.11 -easy-thumbnails==2.3 -django-image-cropping==1.1.0 +git+git://github.com/jasperlittle/django-rest-framework-docs +easy-thumbnails==2.5 +django-image-cropping==1.2 git+git://github.com/interlegis/trml2pdf.git libsass==0.11.1 psycopg2-binary==2.7.4 diff --git a/sapl/api/forms.py b/sapl/api/forms.py index 94bc80de4..c36a0c11f 100644 --- a/sapl/api/forms.py +++ b/sapl/api/forms.py @@ -1,3 +1,5 @@ +import logging + from django.db.models import Q from django.forms.fields import CharField, MultiValueField from django.forms.widgets import MultiWidget, TextInput @@ -124,6 +126,7 @@ class AutorSearchForFieldFilterSet(AutorChoiceFilterSet): class AutoresPossiveisFilterSet(FilterSet): + logger = logging.getLogger(__name__) data_relativa = DateFilter(method='filter_data_relativa') tipo = MethodFilter() @@ -135,9 +138,12 @@ class AutoresPossiveisFilterSet(FilterSet): return queryset def filter_tipo(self, queryset, value): + try: + self.logger.debug("Tentando obter TipoAutor correspondente à pk {}.".format(value)) tipo = TipoAutor.objects.get(pk=value) except: + self.logger.error("TipoAutor(pk={}) inexistente.".format(value)) raise serializers.ValidationError(_('Tipo de Autor inexistente.')) qs = queryset.filter(tipo=tipo) diff --git a/sapl/api/views.py b/sapl/api/views.py index 3ec527646..b8cafc1dd 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -1,4 +1,4 @@ - +import logging from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.http import Http404 @@ -124,6 +124,7 @@ class AutorListView(ListAPIView): qualquer atributo destes models podem ser passados para busca """ + logger = logging.getLogger(__name__) TR_AUTOR_CHOICE_SERIALIZER = 1 TR_AUTOR_SERIALIZER = 3 @@ -138,6 +139,7 @@ class AutorListView(ListAPIView): @property def tr(self): + username = self.request.user.username try: tr = int(self.request.GET.get ('tr', AutorListView.TR_AUTOR_CHOICE_SERIALIZER)) @@ -145,7 +147,8 @@ class AutorListView(ListAPIView): if tr not in (AutorListView.TR_AUTOR_CHOICE_SERIALIZER, AutorListView.TR_AUTOR_SERIALIZER): return AutorListView.TR_AUTOR_CHOICE_SERIALIZER - except: + except Exception as e: + self.logger.error('user=' + username + '. ' + str(e)) return AutorListView.TR_AUTOR_CHOICE_SERIALIZER return tr @@ -161,6 +164,7 @@ class AutorListView(ListAPIView): class AutoresProvaveisListView(ListAPIView): + logger = logging.getLogger(__name__) permission_classes = (IsAuthenticatedOrReadOnly,) queryset = Autor.objects.all() @@ -171,14 +175,16 @@ class AutoresProvaveisListView(ListAPIView): serializer_class = ChoiceSerializer def get_queryset(self): + params = {'content_type__isnull': False} - + username = self.request.user.username tipo = '' try: tipo = int(self.request.GET.get('tipo', '')) if tipo: params['id'] = tipo - except: + except Exception as e: + self.logger.error('user= ' + username + '. ' + str(e)) pass tipos = TipoAutor.objects.filter(**params) diff --git a/sapl/audiencia/forms.py b/sapl/audiencia/forms.py old mode 100644 new mode 100755 index 9f55d3e6d..ab2f2a6e7 --- a/sapl/audiencia/forms.py +++ b/sapl/audiencia/forms.py @@ -1,13 +1,19 @@ +import logging + from django import forms from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db import transaction from django.utils.translation import ugettext_lazy as _ -from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica +from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica, AnexoAudienciaPublica +from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout + +from crispy_forms.helper import FormHelper +from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.utils import timezone class AudienciaForm(forms.ModelForm): - + logger = logging.getLogger(__name__) data_atual = timezone.now() tipo = forms.ModelChoiceField(required=True, @@ -58,6 +64,7 @@ class AudienciaForm(forms.ModelForm): def clean(self): + cleaned_data = super(AudienciaForm, self).clean() if not self.is_valid(): return cleaned_data @@ -68,6 +75,7 @@ class AudienciaForm(forms.ModelForm): if materia and ano_materia and tipo_materia: try: + self.logger.debug("Tentando obter MateriaLegislativa %s nº %s/%s." % (tipo_materia, materia, ano_materia)) materia = MateriaLegislativa.objects.get( numero=materia, ano=ano_materia, @@ -75,14 +83,19 @@ class AudienciaForm(forms.ModelForm): except ObjectDoesNotExist: msg = _('A matéria %s nº %s/%s não existe no cadastro' ' de matérias legislativas.' % (tipo_materia, materia, ano_materia)) + self.logger.error('A MateriaLegislativa %s nº %s/%s não existe no cadastro' + ' de matérias legislativas.' % (tipo_materia, materia, ano_materia)) raise ValidationError(msg) else: + self.logger.info("MateriaLegislativa %s nº %s/%s obtida com sucesso." % (tipo_materia, materia, ano_materia)) cleaned_data['materia'] = materia else: campos = [materia, tipo_materia, ano_materia] if campos.count(None) + campos.count('') < len(campos): msg = _('Preencha todos os campos relacionados à Matéria Legislativa') + self.logger.error('Algum campo relacionado à MatériaLegislativa %s nº %s/%s \ + não foi preenchido.' % (tipo_materia, materia, ano_materia)) raise ValidationError(msg) if not cleaned_data['numero']: @@ -98,7 +111,32 @@ class AudienciaForm(forms.ModelForm): if self.cleaned_data['hora_inicio'] and self.cleaned_data['hora_fim']: if (self.cleaned_data['hora_fim'] < self.cleaned_data['hora_inicio']): - msg = _('A hora de fim não pode ser anterior a hora de início') + msg = _('A hora de fim ({}) não pode ser anterior a hora ' + 'de início({})'.format(self.cleaned_data['hora_fim'], self.cleaned_data['hora_inicio'])) + self.logger.error('Hora de fim anterior à hora de início.') raise ValidationError(msg) return cleaned_data + + +class AnexoAudienciaPublicaForm(forms.ModelForm): + + class Meta: + model = AnexoAudienciaPublica + fields = ['arquivo', + 'assunto'] + + def __init__(self, *args, **kwargs): + + row1 = to_row( + [('arquivo', 4)]) + + row2 = to_row( + [('assunto', 12)]) + + self.helper = FormHelper() + self.helper.layout = SaplFormLayout( + Fieldset(_('Identificação Básica'), + row1, row2)) + super(AnexoAudienciaPublicaForm, self).__init__( + *args, **kwargs) diff --git a/sapl/audiencia/migrations/0007_anexoaudienciapublica.py b/sapl/audiencia/migrations/0007_anexoaudienciapublica.py new file mode 100644 index 000000000..b322cc741 --- /dev/null +++ b/sapl/audiencia/migrations/0007_anexoaudienciapublica.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2018-10-17 20:27 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import sapl.utils + + +class Migration(migrations.Migration): + + dependencies = [ + ('audiencia', '0006_auto_20180808_0856'), + ] + + operations = [ + migrations.CreateModel( + name='AnexoAudienciaPublica', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nome', models.CharField(max_length=30, verbose_name='Nome')), + ('arquivo', models.FileField(blank=True, null=True, upload_to=sapl.utils.texto_upload_path, verbose_name='Arquivo')), + ('data', models.DateField(blank=True, null=True, verbose_name='Data')), + ('assunto', models.TextField(blank=True, verbose_name='Assunto')), + ('indexacao', models.TextField(blank=True)), + ('audiencia', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='audiencia.AudienciaPublica')), + ], + options={ + 'verbose_name': 'Documento Acessório', + 'verbose_name_plural': 'Documentos Acessórios', + }, + ), + ] diff --git a/sapl/audiencia/migrations/0008_auto_20181023_1051.py b/sapl/audiencia/migrations/0008_auto_20181023_1051.py new file mode 100644 index 000000000..70c491a4c --- /dev/null +++ b/sapl/audiencia/migrations/0008_auto_20181023_1051.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.8 on 2018-10-23 13:51 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('audiencia', '0007_anexoaudienciapublica'), + ] + + operations = [ + migrations.AlterModelOptions( + name='anexoaudienciapublica', + options={'verbose_name': 'Anexo de Documento Acessório', 'verbose_name_plural': 'Anexo de Documentos Acessórios'}, + ), + migrations.RemoveField( + model_name='anexoaudienciapublica', + name='nome', + ), + migrations.AlterField( + model_name='anexoaudienciapublica', + name='data', + field=models.DateField(auto_now=True, null=True), + ), + ] diff --git a/sapl/audiencia/migrations/0009_remove_anexoaudienciapublica_indexacao.py b/sapl/audiencia/migrations/0009_remove_anexoaudienciapublica_indexacao.py new file mode 100644 index 000000000..e35a7a2a7 --- /dev/null +++ b/sapl/audiencia/migrations/0009_remove_anexoaudienciapublica_indexacao.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.8 on 2018-10-23 13:55 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('audiencia', '0008_auto_20181023_1051'), + ] + + operations = [ + migrations.RemoveField( + model_name='anexoaudienciapublica', + name='indexacao', + ), + ] diff --git a/sapl/audiencia/models.py b/sapl/audiencia/models.py old mode 100644 new mode 100755 index 477a442d9..684f27625 --- a/sapl/audiencia/models.py +++ b/sapl/audiencia/models.py @@ -1,5 +1,6 @@ import reversion from django.db import models +from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from model_utils import Choices from sapl.materia.models import MateriaLegislativa @@ -143,6 +144,51 @@ class AudienciaPublica(models.Model): self.upload_ata = upload_ata self.upload_anexo = upload_anexo + return models.Model.save(self, force_insert=force_insert, + force_update=force_update, + using=using, + update_fields=update_fields) + + +@reversion.register() +class AnexoAudienciaPublica(models.Model): + audiencia = models.ForeignKey(AudienciaPublica, + on_delete=models.PROTECT) + arquivo = models.FileField( + blank=True, + null=True, + upload_to=texto_upload_path, + verbose_name=_('Arquivo')) + data = models.DateField(auto_now=timezone.now,blank=True, null=True) + assunto = models.TextField( + blank=True, verbose_name=_('Assunto')) + + class Meta: + verbose_name = _('Anexo de Documento Acessório') + verbose_name_plural = _('Anexo de Documentos Acessórios') + + def __str__(self): + return self.assunto + + def delete(self, using=None, keep_parents=False): + if self.arquivo: + self.arquivo.delete() + + return models.Model.delete( + self, using=using, keep_parents=keep_parents) + + def save(self, force_insert=False, force_update=False, using=None, + update_fields=None): + + if not self.pk and self.arquivo: + arquivo = self.arquivo + self.arquivo = None + models.Model.save(self, force_insert=force_insert, + force_update=force_update, + using=using, + update_fields=update_fields) + self.arquivo = arquivo + return models.Model.save(self, force_insert=force_insert, force_update=force_update, using=using, diff --git a/sapl/audiencia/urls.py b/sapl/audiencia/urls.py old mode 100644 new mode 100755 index ece3bf1fb..bdf02beca --- a/sapl/audiencia/urls.py +++ b/sapl/audiencia/urls.py @@ -1,10 +1,11 @@ from django.conf.urls import include, url -from sapl.audiencia.views import (index, AudienciaCrud) +from sapl.audiencia.views import (index, AudienciaCrud,AnexoAudienciaPublicaCrud) from .apps import AppConfig app_name = AppConfig.name urlpatterns = [ - url(r'^audiencia/', include(AudienciaCrud.get_urls())), + url(r'^audiencia/', include(AudienciaCrud.get_urls() + + AnexoAudienciaPublicaCrud.get_urls())), ] \ No newline at end of file diff --git a/sapl/audiencia/views.py b/sapl/audiencia/views.py old mode 100644 new mode 100755 index c999cf4ff..93d214683 --- a/sapl/audiencia/views.py +++ b/sapl/audiencia/views.py @@ -1,10 +1,13 @@ +import sapl + from django.http import HttpResponse +from django.core.urlresolvers import reverse from django.views.decorators.clickjacking import xframe_options_exempt from django.views.generic import UpdateView -from sapl.crud.base import RP_DETAIL, RP_LIST, Crud +from sapl.crud.base import RP_DETAIL, RP_LIST, Crud, MasterDetailCrud -from .forms import AudienciaForm -from .models import AudienciaPublica +from .forms import AudienciaForm, AnexoAudienciaPublicaForm +from .models import AudienciaPublica, AnexoAudienciaPublica def index(request): @@ -17,12 +20,29 @@ class AudienciaCrud(Crud): class BaseMixin(Crud.BaseMixin): list_field_names = ['numero', 'nome', 'tipo', 'materia', - 'data'] + 'data'] ordering = 'nome', 'numero', 'tipo', 'data' class ListView(Crud.ListView): paginate_by = 10 + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + audiencia_materia = {} + for o in context['object_list']: + # indexado pelo numero da audiencia + audiencia_materia[str(o.numero)] = o.materia + + for row in context['rows']: + coluna_materia = row[3] # se mudar a ordem de listagem mudar aqui + if coluna_materia[0]: + materia = audiencia_materia[row[0][0]] + url_materia = reverse('sapl.materia:materialegislativa_detail', + kwargs={'pk': materia.id}) + row[3] = (coluna_materia[0], url_materia) + return context + class CreateView(Crud.CreateView): form_class = AudienciaForm @@ -51,4 +71,40 @@ class AudienciaCrud(Crud): def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) + +class AudienciaPublicaMixin: + + def has_permission(self): + app_config = sapl.base.models.AppConfig.objects.last() + if app_config and app_config.documentos_administrativos == 'O': + return True + + return super().has_permission() + + +class AnexoAudienciaPublicaCrud(MasterDetailCrud): + model = AnexoAudienciaPublica + parent_field = 'audiencia' + help_topic = 'numeracao_docsacess' + + class BaseMixin(MasterDetailCrud.BaseMixin): + list_field_names = ['assunto'] + + class CreateView(MasterDetailCrud.CreateView): + form_class = AnexoAudienciaPublicaForm + layout_key = None + + class UpdateView(MasterDetailCrud.UpdateView): + form_class = AnexoAudienciaPublicaForm + + class ListView(AudienciaPublicaMixin, MasterDetailCrud.ListView): + + def get_queryset(self): + qs = super(MasterDetailCrud.ListView, self).get_queryset() + kwargs = {self.crud.parent_field: self.kwargs['pk']} + return qs.filter(**kwargs).order_by('-data', '-id') + + class DetailView(AudienciaPublicaMixin, + MasterDetailCrud.DetailView): + pass \ No newline at end of file diff --git a/sapl/base/forms.py b/sapl/base/forms.py index 028f2c464..4ba3c6255 100644 --- a/sapl/base/forms.py +++ b/sapl/base/forms.py @@ -1,4 +1,6 @@ import django_filters +import logging + from crispy_forms.bootstrap import FieldWithButtons, InlineRadios, StrictButton from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Button, Div, Field, Fieldset, Layout, Row @@ -53,7 +55,7 @@ def get_roles(): class UsuarioCreateForm(ModelForm): - + logger = logging.getLogger(__name__) username = forms.CharField(required=True, label="Nome de usuário", max_length=30) firstname = forms.CharField(required=True, label="Nome", max_length=30) @@ -81,6 +83,7 @@ class UsuarioCreateForm(ModelForm): data = self.cleaned_data if data['password1'] != data['password2']: + self.logger.error('Erro de validação. Senhas informadas ({}, {}) são diferentes.'.format(data['password1'], data['password2'])) raise ValidationError('Senhas informadas são diferentes') return data @@ -113,8 +116,8 @@ class UsuarioCreateForm(ModelForm): class UsuarioEditForm(ModelForm): + logger = logging.getLogger(__name__) # ROLES = [(g.id, g.name) for g in Group.objects.all().order_by('name')] - ROLES = [] password1 = forms.CharField(required=False, widget=forms.PasswordInput, label='Senha') @@ -155,17 +158,19 @@ class UsuarioEditForm(ModelForm): data = self.cleaned_data if data['password1'] and data['password1'] != data['password2']: + self.logger.error('Erro de validação. Senhas informadas ({}, {}) são diferentes.'.format(data['password1'], data['password2'])) raise ValidationError('Senhas informadas são diferentes') return data class SessaoLegislativaForm(ModelForm): - + logger = logging.getLogger(__name__) class Meta: model = SessaoLegislativa exclude = [] def clean(self): + cleaned_data = super(SessaoLegislativaForm, self).clean() if not self.is_valid(): @@ -179,7 +184,7 @@ class SessaoLegislativaForm(ModelForm): data_inicio_leg = legislatura.data_inicio data_fim_leg = legislatura.data_fim pk = self.initial['id'] if self.initial else None - # Queries para verificar se existem Sessões Legislativas no período selecionado no form + # Queries para verificar se existem Sessões Legislativas no período selecionado no form # Caso onde a data_inicio e data_fim são iguais a de alguma sessão já criada primeiro_caso = Q(data_inicio=data_inicio, data_fim=data_fim) # Caso onde a data_inicio está entre o início e o fim de uma Sessão já existente @@ -209,20 +214,29 @@ class SessaoLegislativaForm(ModelForm): ult = 0 if numero <= ult and flag_edit: + self.logger.error('O número da SessaoLegislativa ({}) é menor ou igual ' + 'que o de Sessões Legislativas passadas ({})'.format(numero, ult)) raise ValidationError('O número da Sessão Legislativa não pode ser menor ou igual ' 'que o de Sessões Legislativas passadas') if data_inicio < data_inicio_leg or \ data_inicio > data_fim_leg: + self.logger.error('A data de início ({}) da SessaoLegislativa está compreendida ' + 'fora da data início ({}) e fim ({}) da Legislatura ' + 'selecionada'.format(data_inicio, data_inicio_leg, data_fim_leg)) raise ValidationError('A data de início da Sessão Legislativa deve estar compreendida ' 'entre a data início e fim da Legislatura selecionada') if data_fim > data_fim_leg or \ data_fim < data_inicio_leg: + self.logger.error('A data de fim ({}) da SessaoLegislativa está compreendida ' + 'fora da data início ({}) e fim ({}) da Legislatura ' + 'selecionada.'.format(data_fim, data_inicio_leg, data_fim_leg)) raise ValidationError('A data de fim da Sessão Legislativa deve estar compreendida ' 'entre a data início e fim da Legislatura selecionada') if data_inicio > data_fim: + self.logger.error('Data início ({}) superior à data fim ({}).'.format(data_inicio, data_fim)) raise ValidationError('Data início não pode ser superior à data fim') data_inicio_intervalo = cleaned_data['data_inicio_intervalo'] @@ -230,6 +244,8 @@ class SessaoLegislativaForm(ModelForm): if data_inicio_intervalo and data_fim_intervalo and \ data_inicio_intervalo > data_fim_intervalo: + self.logger.error('Data início de intervalo ({}) superior à ' + 'data fim de intervalo ({}).'.format(data_inicio_intervalo, data_fim_intervalo)) raise ValidationError('Data início de intervalo não pode ser ' 'superior à data fim de intervalo') @@ -238,6 +254,10 @@ class SessaoLegislativaForm(ModelForm): data_inicio_intervalo < data_inicio_leg or \ data_inicio_intervalo > data_fim or \ data_inicio_intervalo > data_fim_leg: + self.logger.error('A data de início do intervalo ({}) não está compreendida entre ' + 'as datas de início ({}) e fim ({}) tanto da Legislatura quanto da ' + 'própria Sessão Legislativa ({} e {}).' + .format(data_inicio_intervalo, data_inicio_leg, data_fim_leg, data_inicio, data_fim)) raise ValidationError('A data de início do intervalo deve estar compreendida entre ' 'as datas de início e fim tanto da Legislatura quanto da ' 'própria Sessão Legislativa') @@ -246,6 +266,10 @@ class SessaoLegislativaForm(ModelForm): data_fim_intervalo > data_fim_leg or \ data_fim_intervalo < data_inicio or \ data_fim_intervalo < data_inicio_leg: + self.logger.error('A data de fim do intervalo ({}) não está compreendida entre ' + 'as datas de início ({}) e fim ({}) tanto da Legislatura quanto da ' + 'própria Sessão Legislativa ({} e {}).' + .format(data_fim_intervalo, data_inicio_leg, data_fim_leg, data_inicio, data_fim)) raise ValidationError('A data de fim do intervalo deve estar compreendida entre ' 'as datas de início e fim tanto da Legislatura quanto da ' 'própria Sessão Legislativa') @@ -262,8 +286,36 @@ class TipoAutorForm(ModelForm): super(TipoAutorForm, self).__init__(*args, **kwargs) + def clean(self): + super(TipoAutorForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + cd = self.cleaned_data + lista = ['comissão', + 'comis', + 'parlamentar', + 'bancada', + 'bloco', + 'comissao', + 'vereador', + 'órgão', + 'orgao', + 'deputado', + 'senador', + 'vereadora', + 'frente'] + + for l in lista: + if l in cd['descricao'].lower(): + raise ValidationError(_('A descrição colocada não pode ser usada ' + 'por ser equivalente a um tipo já existente')) + class AutorForm(ModelForm): + logger = logging.getLogger(__name__) + senha = forms.CharField( max_length=20, label=_('Senha'), @@ -411,6 +463,7 @@ class AutorForm(ModelForm): def valida_igualdade(self, texto1, texto2, msg): if texto1 != texto2: + self.logger.error('Textos diferentes. ("{}" e "{}")'.format(texto1, texto2)) raise ValidationError(msg) return True @@ -424,6 +477,8 @@ class AutorForm(ModelForm): cd = self.cleaned_data if 'action_user' not in cd or not cd['action_user']: + self.logger.error('Não Informado se o Autor terá usuário ' + 'vinculado para acesso ao Sistema.') raise ValidationError(_('Informe se o Autor terá usuário ' 'vinculado para acesso ao Sistema.')) @@ -433,6 +488,10 @@ class AutorForm(ModelForm): self.instance.user, get_user_model().USERNAME_FIELD) != cd['username']: if 'status_user' not in cd or not cd['status_user']: + self.logger.error('Foi trocado ou removido o usuário deste Autor ({}), ' + 'mas não foi informado como se deve proceder ' + 'com o usuário que está sendo desvinculado? ({})' + .format(cd['username'], get_user_model().USERNAME_FIELD)) raise ValidationError( _('Foi trocado ou removido o usuário deste Autor, ' 'mas não foi informado como se deve proceder ' @@ -449,6 +508,7 @@ class AutorForm(ModelForm): if cd['action_user'] == 'A': param_username = {get_user_model().USERNAME_FIELD: cd['username']} if not User.objects.filter(**param_username).exists(): + self.logger.error('Não existe usuário com username "%s". ' % cd['username']) raise ValidationError( _('Não existe usuário com username "%s". ' 'Para utilizar esse username você deve selecionar ' @@ -457,11 +517,13 @@ class AutorForm(ModelForm): if cd['action_user'] != 'N': if 'username' not in cd or not cd['username']: + self.logger.error('Username não informado.') raise ValidationError(_('O username deve ser informado.')) param_username = { 'user__' + get_user_model().USERNAME_FIELD: cd['username']} if qs_autor.filter(**param_username).exists(): + self.logger.error('Já existe um Autor para este usuário ({}).'.format(cd['username'])) raise ValidationError( _('Já existe um Autor para este usuário.')) @@ -471,6 +533,7 @@ class AutorForm(ModelForm): ainda assim para renderizar um message.danger no topo do form. """ if 'tipo' not in cd or not cd['tipo']: + self.logger.error('Tipo do Autor não selecionado.') raise ValidationError( _('O Tipo do Autor deve ser selecionado.')) @@ -478,16 +541,21 @@ class AutorForm(ModelForm): if not tipo.content_type: if 'nome' not in cd or not cd['nome']: + self.logger.error('Nome do Autor não informado.') raise ValidationError( _('O Nome do Autor deve ser informado.')) else: if 'autor_related' not in cd or not cd['autor_related']: + self.logger.error('Registro de %s não escolhido para ser ' + 'vinculado ao cadastro de Autor' % tipo.descricao) raise ValidationError( _('Um registro de %s deve ser escolhido para ser ' 'vinculado ao cadastro de Autor') % tipo.descricao) if not tipo.content_type.model_class().objects.filter( pk=cd['autor_related']).exists(): + self.logger.error('O Registro definido (%s-%s) não está na base ' + 'de %s.' % (cd['autor_related'], cd['q'], tipo.descricao)) raise ValidationError( _('O Registro definido (%s-%s) não está na base de %s.' ) % (cd['autor_related'], cd['q'], tipo.descricao)) @@ -497,6 +565,8 @@ class AutorForm(ModelForm): content_type_id=cd['tipo'].content_type_id) if qs_autor_selected.exists(): autor = qs_autor_selected.first() + self.logger.error('Já existe um autor Cadastrado para ' + '%s' % autor.autor_related) raise ValidationError( _('Já existe um autor Cadastrado para %s' ) % autor.autor_related) @@ -967,6 +1037,7 @@ class LoginForm(AuthenticationForm): class ConfiguracoesAppForm(ModelForm): + logger = logging.getLogger(__name__) mostrar_brasao_painel = forms.BooleanField( help_text=_('Sugerimos fortemente que faça o upload de imagens com ' @@ -1005,9 +1076,12 @@ class ConfiguracoesAppForm(ModelForm): casa = CasaLegislativa.objects.first() if not casa: - raise ValidationError("Não há casa legislativa relacionada") + 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): + self.logger.error('Não há logitipo configurado para esta ' + 'CasaLegislativa ({}).'.format(casa)) raise ValidationError("Não há logitipo configurado para esta " "Casa legislativa.") @@ -1016,6 +1090,8 @@ class ConfiguracoesAppForm(ModelForm): class RecuperarSenhaForm(PasswordResetForm): + logger = logging.getLogger(__name__) + def __init__(self, *args, **kwargs): row1 = to_row( [('email', 12)]) @@ -1039,6 +1115,8 @@ class RecuperarSenhaForm(PasswordResetForm): if not email_existente: msg = 'Não existe nenhum usuário cadastrado com este e-mail.' + self.logger.error('Não existe nenhum usuário cadastrado com este e-mail ({}).' + .format(self.data['email'])) raise ValidationError(msg) return self.cleaned_data @@ -1061,6 +1139,7 @@ class NovaSenhaForm(SetPasswordForm): class AlterarSenhaForm(Form): + logger = logging.getLogger(__name__) username = forms.CharField(widget=forms.HiddenInput()) @@ -1104,6 +1183,7 @@ class AlterarSenhaForm(Form): new_password2 = data['new_password2'] if new_password1 != new_password2: + self.logger.error("'Nova Senha' ({}) diferente de 'Confirmar Senha' ({})".format(new_password1, new_password2)) raise ValidationError("'Nova Senha' diferente de 'Confirmar Senha'") # TODO: colocar mais regras como: tamanho mínimo, @@ -1111,6 +1191,7 @@ class AlterarSenhaForm(Form): # TODO: senha atual igual a senha anterior, etc if len(new_password1) < 6: + self.logger.error('A senha informada ({}) não tem o mínimo de 6 caracteres.'.format(new_password1)) raise ValidationError("A senha informada deve ter no mínimo 6 caracteres") username = data['username'] @@ -1118,13 +1199,17 @@ class AlterarSenhaForm(Form): user = User.objects.get(username=username) if user.is_anonymous(): + self.logger.error('Não é possível alterar senha de usuário anônimo ({}).'.format(username)) raise ValidationError("Não é possível alterar senha de usuário anônimo") if not user.check_password(old_password): + self.logger.error('Senha atual informada ({}) não confere ' + 'com a senha armazenada.'.format(old_password)) raise ValidationError("Senha atual informada não confere " "com a senha armazenada") if user.check_password(new_password1): + self.logger.error('Nova senha ({}) igual à senha anterior.'.format(new_password1)) raise ValidationError("Nova senha não pode ser igual à senha anterior") return self.cleaned_data diff --git a/sapl/base/templatetags/common_tags.py b/sapl/base/templatetags/common_tags.py index 71f63e130..99582016d 100644 --- a/sapl/base/templatetags/common_tags.py +++ b/sapl/base/templatetags/common_tags.py @@ -6,7 +6,7 @@ from sapl.base.models import AppConfig from sapl.materia.models import DocumentoAcessorio, MateriaLegislativa, Proposicao from sapl.norma.models import NormaJuridica from sapl.parlamentares.models import Filiacao -from sapl.utils import filiacao_data +from sapl.utils import filiacao_data, SEPARADOR_HASH_PROPOSICAO register = template.Library() @@ -96,7 +96,12 @@ def isinst(value, class_str): @register.filter @stringfilter def strip_hash(value): - return value.split('/')[0][1:] + vet = value.split('/') + if len(vet) == 2: + return vet[0][1:] + else: + return value.split(SEPARADOR_HASH_PROPOSICAO)[0][1:] + @register.filter @@ -198,6 +203,19 @@ def url(value): return True return False +@register.filter +def audio_url(value): + return True if url(value) and value.endswith("mp3") else False + + +@register.filter +def video_url(value): + return True if url(value) and value.endswith("mp4") else False + +@register.filter +def file_extension(value): + import pathlib + return pathlib.Path(value).suffix.replace('.', '') @register.filter def cronometro_to_seconds(value): diff --git a/sapl/base/tests/teststub_urls.py b/sapl/base/tests/teststub_urls.py index 9796768f2..8b66d6586 100644 --- a/sapl/base/tests/teststub_urls.py +++ b/sapl/base/tests/teststub_urls.py @@ -1,11 +1,9 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url from django.views.generic.base import TemplateView from sapl.urls import urlpatterns as original_patterns -ptrn = patterns('', - url(r'^zzzz$', +ptrn = [url(r'^zzzz$', TemplateView.as_view( - template_name='index.html'), name='zzzz')) - + template_name='index.html'), name='zzzz')] urlpatterns = original_patterns + ptrn diff --git a/sapl/base/views.py b/sapl/base/views.py index 2ce1abf92..bb6dda168 100644 --- a/sapl/base/views.py +++ b/sapl/base/views.py @@ -1,4 +1,5 @@ import os +import logging from django.contrib.auth import get_user_model from django.contrib.auth.mixins import PermissionRequiredMixin @@ -146,6 +147,7 @@ class AutorCrud(CrudAux): return CrudAux.DeleteView.delete(self, *args, **kwargs) class UpdateView(CrudAux.UpdateView): + logger = logging.getLogger(__name__) layout_key = None form_class = AutorForm @@ -164,10 +166,13 @@ class AutorCrud(CrudAux): return CrudAux.UpdateView.get(self, request, *args, **kwargs) def get_success_url(self): + username = self.request.user.username pk_autor = self.object.id url_reverse = reverse('sapl.base:autor_detail', kwargs={'pk': pk_autor}) + try: + self.logger.debug('user=' + username + '. Enviando email na edição de Autores.') kwargs = {} user = self.object.user @@ -194,12 +199,13 @@ class AutorCrud(CrudAux): destinatario = [user.email] send_mail(assunto, mensagem, remetente, destinatario, fail_silently=False) - except: - print( - _('Erro no envio de email na edição de Autores.')) + except Exception as e: + self.logger.error('user=' + username + '. Erro no envio de email na edição de Autores. ' + str(e)) + return url_reverse class CreateView(CrudAux.CreateView): + logger = logging.getLogger(__name__) form_class = AutorForm layout_key = None @@ -214,10 +220,14 @@ class AutorCrud(CrudAux): return CrudAux.CreateView.get(self, request, *args, **kwargs) def get_success_url(self): + username = self.request.user.username pk_autor = self.object.id url_reverse = reverse('sapl.base:autor_detail', kwargs={'pk': pk_autor}) + try: + self.logger.debug('user=' + username + '. Enviando email na criação de Autores.') + kwargs = {} user = self.object.user @@ -244,9 +254,11 @@ class AutorCrud(CrudAux): destinatario = [user.email] send_mail(assunto, mensagem, remetente, destinatario, fail_silently=False) - except: + except Exception as e: print( _('Erro no envio de email na criação de Autores.')) + self.logger.error('user=' + username + '. Erro no envio de email na criação de Autores. ' + str(e)) + return url_reverse @@ -276,11 +288,13 @@ class RelatorioAtasView(FilterView): class RelatorioPresencaSessaoView(FilterView): + logger = logging.getLogger(__name__) model = SessaoPlenaria filterset_class = RelatorioPresencaSessaoFilterSet template_name = 'base/RelatorioPresencaSessao_filter.html' def get_context_data(self, **kwargs): + context = super(RelatorioPresencaSessaoView, self).get_context_data(**kwargs) context['title'] = _('Presença dos parlamentares nas sessões') @@ -289,7 +303,6 @@ class RelatorioPresencaSessaoView(FilterView): if not self.filterset.form.is_valid(): return context - # ===================================================================== # if 'salvar' not in self.request.GET: where = context['object_list'].query.where _range = where.children[0].rhs @@ -323,6 +336,8 @@ class RelatorioPresencaSessaoView(FilterView): total_sessao = context['object_list'].count() + username = self.request.user.username + # Completa o dicionario as informacoes parlamentar/sessao/ordem parlamentares_presencas = [] for i, p in enumerate(parlamentares_qs): @@ -332,12 +347,18 @@ class RelatorioPresencaSessaoView(FilterView): 'ordemdia_porc': 0 }) try: + self.logger.debug('user=' + username + '. Tentando obter presença do parlamentar (pk={}).'.format(p.id)) sessao_count = presenca_sessao.get(parlamentar_id=p.id)[1] - except ObjectDoesNotExist: + except ObjectDoesNotExist as e: + self.logger.error('user=' + username + '. Erro ao obter presença do parlamentar (pk={}). Definido como 0. '.format(p.id) + str(e)) sessao_count = 0 try: + # Presenças de cada Ordem do Dia + self.logger.info('user=' + username + '. Tentando obter PresencaOrdemDia para o parlamentar pk={}.'.format(p.id)) ordemdia_count = presenca_ordem.get(parlamentar_id=p.id)[1] except ObjectDoesNotExist: + self.logger.error('user=' + username + '. Erro ao obter PresencaOrdemDia para o parlamentar pk={}. ' + 'Definido como 0.'.format(p.id)) ordemdia_count = 0 parlamentares_presencas[i].update({ @@ -847,12 +868,17 @@ class CasaLegislativaCrud(CrudAux): class HelpTopicView(TemplateView): + logger = logging.getLogger(__name__) def get_template_names(self): + + username = self.request.user.username topico = self.kwargs['topic'] try: + self.logger.debug('user=' + username + '. Tentando obter template %s.html.' % topico) get_template('ajuda/%s.html' % topico) - except TemplateDoesNotExist: + except TemplateDoesNotExist as e: + self.logger.error('user=' + username + '. Erro ao obter template {}.html. Template não existe. '.format(topico) + str(e)) raise Http404() return ['ajuda/%s.html' % topico] diff --git a/sapl/comissoes/forms.py b/sapl/comissoes/forms.py index 94d9b081f..7d230a7d4 100644 --- a/sapl/comissoes/forms.py +++ b/sapl/comissoes/forms.py @@ -1,3 +1,5 @@ +import logging + from django import forms from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError @@ -14,7 +16,7 @@ from sapl.parlamentares.models import Legislatura, Mandato, Parlamentar class ComposicaoForm(forms.ModelForm): comissao = forms.CharField(required=False, label='Comissao', widget=forms.HiddenInput()) - + logger = logging.getLogger(__name__) class Meta: model = Composicao exclude = [] @@ -39,6 +41,9 @@ class ComposicaoForm(forms.ModelForm): comissao_id=comissao_pk) if intersecao_periodo: + self.logger.error('O período informado ({} a {})' + 'choca com períodos já ' + 'cadastrados para esta comissão'.format(periodo.data_inicio, periodo.data_fim)) raise ValidationError('O período informado ' 'choca com períodos já ' 'cadastrados para esta comissão') @@ -48,6 +53,7 @@ class ComposicaoForm(forms.ModelForm): class PeriodoForm(forms.ModelForm): + logger = logging.getLogger(__name__) class Meta: model = Periodo exclude = [] @@ -62,8 +68,10 @@ class PeriodoForm(forms.ModelForm): data_fim = cleaned_data['data_fim'] if data_fim and data_fim < data_inicio: + self.logger.error('A Data Final ({}) é menor que ' + 'a Data Inicial({}).'.format(data_fim, data_inicio)) raise ValidationError('A Data Final não pode ser menor que ' - 'a Data Inicial') + 'a Data Inicial') # Evita NoneType exception se não preenchida a data_fim if not data_fim: @@ -74,6 +82,9 @@ class PeriodoForm(forms.ModelForm): ) if not legislatura: + self.logger.error('O período informado ({} a {})' + 'não está contido em uma única ' + 'legislatura existente'.format(data_inicio, data_fim)) raise ValidationError('O período informado ' 'deve estar contido em uma única ' 'legislatura existente') @@ -85,6 +96,7 @@ class PeriodoForm(forms.ModelForm): class ParticipacaoCreateForm(forms.ModelForm): + logger = logging.getLogger(__name__) parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput()) class Meta: @@ -135,7 +147,9 @@ class ParticipacaoCreateForm(forms.ModelForm): data_desligamento = cleaned_data['data_desligamento'] if data_desligamento and \ - data_designacao > data_desligamento: + data_designacao > data_desligamento: + self.logger.error('Data de designação ({}) superior ' + 'à data de desligamento ({})'.format(data_designacao, data_desligamento)) raise ValidationError(_('Data de designação não pode ser superior ' 'à data de desligamento')) @@ -144,6 +158,7 @@ class ParticipacaoCreateForm(forms.ModelForm): if cleaned_data['cargo'].nome in cargos_unicos: msg = _('Este cargo é único para esta Comissão.') + self.logger.error('Este cargo ({}) é único para esta Comissão.'.format(cleaned_data['cargo'].nome)) raise ValidationError(msg) return cleaned_data @@ -190,6 +205,7 @@ class ParticipacaoCreateForm(forms.ModelForm): class ParticipacaoEditForm(forms.ModelForm): + logger = logging.getLogger(__name__) parent_pk = forms.CharField(required=False) # widget=forms.HiddenInput()) nome_parlamentar = forms.CharField(required=False, label='Parlamentar') @@ -219,6 +235,8 @@ class ParticipacaoEditForm(forms.ModelForm): if data_desligamento and \ data_designacao > data_desligamento: + self.logger.error('Data de designação ({}) superior ' + 'à data de desligamento ({})'.format(data_designacao, data_desligamento)) raise ValidationError(_('Data de designação não pode ser superior ' 'à data de desligamento')) @@ -229,6 +247,8 @@ class ParticipacaoEditForm(forms.ModelForm): if cleaned_data['cargo'].nome in cargos_unicos: msg = _('Este cargo é único para esta Comissão.') + self.logger.error('Este cargo ({}) é único para esta Comissão (id={}).' + .format(cleaned_data['cargo'].nome, composicao_id)) raise ValidationError(msg) return cleaned_data @@ -236,6 +256,7 @@ class ParticipacaoEditForm(forms.ModelForm): class ComissaoForm(forms.ModelForm): + logger = logging.getLogger(__name__) class Meta: model = Comissao fields = '__all__' @@ -260,37 +281,50 @@ class ComissaoForm(forms.ModelForm): return self.cleaned_data if len(self.cleaned_data['nome']) > 100: - msg = _('Nome da Comissão deve ter no máximo 50 caracteres.') + msg = _('Nome da Comissão informado ({}) tem mais de 50 caracteres.'.format(self.cleaned_data['nome'])) + self.logger.error('Nome da Comissão deve ter no máximo 50 caracteres.') raise ValidationError(msg) if (self.cleaned_data['data_extincao'] and self.cleaned_data['data_extincao'] < self.cleaned_data['data_criacao']): msg = _('Data de extinção não pode ser menor que a de criação') + self.logger.error('Data de extinção ({}) não pode ser menor que a de criação ({}).' + .format(self.cleaned_data['data_extincao'],self.cleaned_data['data_criacao'])) raise ValidationError(msg) if (self.cleaned_data['data_final_prevista_temp'] and self.cleaned_data['data_final_prevista_temp'] < self.cleaned_data['data_criacao']): msg = _('Data Prevista para Término não pode ser menor que a de criação') + self.logger.error('Data Prevista para Término ({}) não pode ser menor que a de criação ({}).' + .format(self.cleaned_data['data_final_prevista_temp'], self.cleaned_data['data_criacao'])) raise ValidationError(msg) if (self.cleaned_data['data_prorrogada_temp'] and self.cleaned_data['data_prorrogada_temp'] < self.cleaned_data['data_criacao']): msg = _('Data Novo Prazo não pode ser menor que a de criação') + self.logger.error('Data Novo Prazo ({}) não pode ser menor que a de criação ({}).' + .format(self.cleaned_data['data_prorrogada_temp'], self.cleaned_data['data_criacao'])) raise ValidationError(msg) if (self.cleaned_data['data_instalacao_temp'] and self.cleaned_data['data_instalacao_temp'] < self.cleaned_data['data_criacao']): msg = _('Data de Instalação não pode ser menor que a de criação') + self.logger.error('Data de Instalação ({}) não pode ser menor que a de criação ({}).' + .format(self.cleaned_data['data_instalacao_temp'], self.cleaned_data['data_criacao'])) raise ValidationError(msg) if (self.cleaned_data['data_final_prevista_temp'] and self.cleaned_data['data_instalacao_temp'] and self.cleaned_data['data_final_prevista_temp'] < self.cleaned_data['data_instalacao_temp']): - msg = _('Data Prevista para Término não pode ser menor que a de Instalação') + msg = _('Data Prevista para Término não pode ser menor que a de Instalação.') + self.logger.error('Data Prevista para Término ({}) não pode ser menor que a de Instalação ({}).' + .format(self.cleaned_data['data_final_prevista_temp'], self.cleaned_data['data_instalacao_temp'])) raise ValidationError(msg) if (self.cleaned_data['data_prorrogada_temp'] and self.cleaned_data['data_instalacao_temp'] and self.cleaned_data['data_prorrogada_temp'] < self.cleaned_data['data_instalacao_temp']): - msg = _('Data Novo Prazo não pode ser menor que a de Instalação') + msg = _('Data Novo Prazo não pode ser menor que a de Instalação.') + self.logger.error('Data Novo Prazo ({}) não pode ser menor que a de Instalação ({}).' + .format(self.cleaned_data['data_prorrogada_temp'], self.cleaned_data['data_instalacao_temp'])) raise ValidationError(msg) return self.cleaned_data @@ -317,6 +351,7 @@ class ComissaoForm(forms.ModelForm): class ReuniaoForm(ModelForm): + logger = logging.getLogger(__name__) comissao = forms.ModelChoiceField(queryset=Comissao.objects.all(), widget=forms.HiddenInput()) @@ -326,6 +361,7 @@ class ReuniaoForm(ModelForm): def clean(self): super(ReuniaoForm, self).clean() + if not self.is_valid(): return self.cleaned_data @@ -334,6 +370,8 @@ class ReuniaoForm(ModelForm): if (self.cleaned_data['hora_fim'] < self.cleaned_data['hora_inicio']): msg = _('A hora de término da reunião não pode ser menor que a de início') + self.logger.error("A hora de término da reunião ({}) não pode ser menor que a de início ({})." + .format(self.cleaned_data['hora_fim'], self.cleaned_data['hora_inicio'])) raise ValidationError(msg) return self.cleaned_data diff --git a/sapl/comissoes/tests/test_comissoes.py b/sapl/comissoes/tests/test_comissoes.py index 4d12ba810..d2f8b0bd1 100644 --- a/sapl/comissoes/tests/test_comissoes.py +++ b/sapl/comissoes/tests/test_comissoes.py @@ -74,6 +74,7 @@ def test_incluir_comissao_submit(admin_client): 'nome': 'Comissão Teste', 'sigla': 'CT', 'data_criacao': '2016-03-22', + 'unidade_deliberativa': True, 'salvar': 'salvar'}, follow=True) assert response.status_code == 200 diff --git a/sapl/comissoes/views.py b/sapl/comissoes/views.py index 78e639168..743eaa58f 100644 --- a/sapl/comissoes/views.py +++ b/sapl/comissoes/views.py @@ -1,3 +1,4 @@ +import logging from django.core.urlresolvers import reverse from django.db.models import F @@ -102,13 +103,18 @@ class ComposicaoCrud(MasterDetailCrud): return {'comissao': comissao} class ListView(MasterDetailCrud.ListView): + logger = logging.getLogger(__name__) template_name = "comissoes/composicao_list.html" 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.') return int(self.request.GET['pk']) - except: + except Exception as e: + self.logger.error('user=' + username + '. Erro ao obter pk da composição. Retornado 0. ' + str(e)) return 0 def get_context_data(self, **kwargs): @@ -193,12 +199,17 @@ class ReuniaoCrud(MasterDetailCrud): list_field_names = ['data', 'nome', 'tema'] class ListView(MasterDetailCrud.ListView): + logger = logging.getLogger(__name__) paginate_by = 10 def take_reuniao_pk(self): + + username = self.request.user.username try: + self.logger.debug('user=' + username + '. Tentando obter pk da reunião.') return int(self.request.GET['pk']) - except: + except Exception as e: + self.logger.error('user=' + username + '. Erro ao obter pk da reunião. Retornado 0. ' + str(e)) return 0 def get_context_data(self, **kwargs): diff --git a/sapl/compilacao/views.py b/sapl/compilacao/views.py index 82b5cbd10..765cd6fa2 100644 --- a/sapl/compilacao/views.py +++ b/sapl/compilacao/views.py @@ -1,6 +1,7 @@ from collections import OrderedDict from datetime import timedelta import sys +import logging from braces.views import FormMessagesMixin from django import forms @@ -650,6 +651,7 @@ class NotaMixin(DispositivoSuccessUrlMixin): class NotasCreateView(NotaMixin, CreateView): + logger = logging.getLogger(__name__) template_name = 'compilacao/ajax_form.html' form_class = NotaForm permission_required = 'compilacao.add_nota' @@ -662,6 +664,8 @@ class NotasCreateView(NotaMixin, CreateView): return super(NotasCreateView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): + + username = request.user.username self.object = None try: ta_id = kwargs.pop('ta_id') @@ -679,7 +683,9 @@ class NotasCreateView(NotaMixin, CreateView): else: return self.form_invalid(form) except Exception as e: + self.logger.error("user=" + username + ". " + str(e)) print(e) + self.logger.error("user=" + username + ". Error post.") return HttpResponse("error post") @@ -756,7 +762,7 @@ class PublicacaoMixin(CompMixin): messages.error(request, _( 'A funcionalidade de %s está desativada para %s.') % ( TipoTextoArticulado._meta.get_field( - 'publicacao_func').verbose_name, + 'publicacao_func').verbose_name, ta.tipo_ta.descricao)) return redirect(reverse('sapl.compilacao:ta_text', kwargs={'ta_id': self.kwargs['ta_id']})) @@ -1469,6 +1475,8 @@ class ActionDragAndMoveDispositivoAlteradoMixin(ActionsCommonsMixin): class ActionDeleteDispositivoMixin(ActionsCommonsMixin): + logger = logging.getLogger(__name__) + def json_delete_item_dispositivo(self, context): return self.json_delete_bloco_dispositivo(context, bloco=False) @@ -1516,6 +1524,7 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): return data def remover_dispositivo(self, base, bloco): + username = self.request.user.username base_ordem = base.ordem if base.dispositivo_subsequente or base.dispositivo_substituido: p = base.dispositivo_substituido @@ -1541,6 +1550,7 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): p.dispositivos_filhos_set.add(d) p.save() except Exception as e: + self.logger.error("user=" + username + '. ' + str(e)) print(e) base.delete() else: @@ -1575,6 +1585,8 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): dispositivo_pai=base).first() if not anterior: + self.logger.error("user=" + username + ". Não é possível excluir este Dispositivo (id={}) sem" + " excluir toda a sua estrutura!!!".format(base.ta_id)) raise Exception( _('Não é possível excluir este Dispositivo sem' ' excluir toda a sua estrutura!!!')) @@ -1594,6 +1606,9 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): for candidato in parents: if candidato == base: + self.logger.error("user=" + username + ". Não é possível excluir este " + "Dispositivo ({}) sem " + "excluir toda a sua estrutura!!!".format(candidato)) raise Exception( _('Não é possível excluir este ' 'Dispositivo sem ' @@ -1629,7 +1644,9 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): d.rotulo = d.rotulo_padrao() break else: - + self.logger.error("user=" + username + ". Não é possível excluir este " + "Dispositivo ({}) sem excluir toda " + "a sua estrutura!!!".format(candidato)) raise Exception( _('Não é possível excluir este ' 'Dispositivo sem ' @@ -1675,7 +1692,8 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): profundidade=profundidade_base) irmao.rotulo = irmao.rotulo_padrao() irmao.save() - except: + except Exception as e: + self.logger.error("user=" + username + "." + str(e)) break irmaos = pai_base.dispositivos_filhos_set.\ @@ -1792,7 +1810,8 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): dr.rotulo = dr.rotulo_padrao() try: dr.save(clean=base != dr) - except: + except Exception as e: + self.logger.error("user=" + username + ". " + str(e)) break # Pode não ser religavável @@ -1836,6 +1855,8 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin): class ActionDispositivoCreateMixin(ActionsCommonsMixin): + logger = logging.getLogger(__name__) + def allowed_inserts(self, _base=None): request = self.request try: @@ -2090,6 +2111,8 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin): return result except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) print(e) return {} @@ -2097,7 +2120,6 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin): def json_set_dvt(self, context): # Dispositivo de Vigência do Texto Original e de Dpts Alterados dvt = Dispositivo.objects.get(pk=self.kwargs['dispositivo_id']) - if dvt.auto_inserido: dvt = dvt.dispositivo_pai @@ -2136,8 +2158,11 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin): 'com sucesso!!!')) return data - except: + except Exception as e: data = {} + username = self.request.user.username + self.logger.error("user=" + username + ". Ocorreu um erro ({}) na atualização do " + "Dispositivo de Vigência".format(str(e))) self.set_message(data, 'success', _('Ocorreu um erro na atualização do ' @@ -2154,8 +2179,9 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin): def json_add_next( self, context, local_add='json_add_next', create_auto_inserts=True): + try: - + dp_auto_insert = None base = Dispositivo.objects.get(pk=self.kwargs['dispositivo_id']) tipo = TipoDispositivo.objects.get(pk=context['tipo_pk']) @@ -2425,6 +2451,8 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin): return data except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) print(e) return {} @@ -2432,6 +2460,7 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin): class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, ActionDeleteDispositivoMixin, ActionDispositivoCreateMixin): + logger = logging.getLogger(__name__) def render_to_json_response(self, context, **response_kwargs): @@ -2698,6 +2727,8 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin, _('Dispositivo de Revogação adicionado com sucesso.')) except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) print(e) data.update({'pk': ndp.pk, @@ -2892,7 +2923,21 @@ class DispositivoSearchFragmentFormView(ListView): itens.append(item) return JsonResponse(itens, safe=False) - return ListView.get(self, request, *args, **kwargs) + response = ListView.get(self, request, *args, **kwargs) + + if not self.object_list.exists(): + messages.info( + request, _('Não foram encontrados resultados ' + 'com seus critérios de busca!')) + + try: + r = response.render() + return response + except Exception as e: + messages.error(request, "Erro - %s" % e) + context = {} + self.template_name = 'compilacao/messages.html' + return self.render_to_response(context) def get_queryset(self): try: @@ -3080,7 +3125,7 @@ class DispositivoEdicaoBasicaView(CompMixin, FormMessagesMixin, UpdateView): 'as alterações no Dispositivo') permission_required = 'compilacao.change_dispositivo_edicao_avancada' - + logger = logging.getLogger(__name__) @property def cancel_url(self): return reverse_lazy( @@ -3122,7 +3167,9 @@ class DispositivoEdicaoBasicaView(CompMixin, FormMessagesMixin, UpdateView): d.set_numero_completo([d.dispositivo0, ] + numero) d.rotulo = d.rotulo_padrao() - except: + except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". Ocorreu erro ({}) na atualização do rótulo.".format(str(e))) return True, JsonResponse({'message': str( _('Ocorreu erro na atualização do rótulo'))}, safe=False) return True, JsonResponse({ @@ -3180,6 +3227,7 @@ class DispositivoDefinidorVigenciaView(CompMixin, FormMessagesMixin, FormView): permission_required = ('compilacao.change_dispositivo_edicao_avancada', 'compilacao.change_dispositivo_de_vigencia_global') + logger = logging.getLogger(__name__) def get_form_kwargs(self): kwargs = FormView.get_form_kwargs(self) @@ -3224,7 +3272,9 @@ class DispositivoDefinidorVigenciaView(CompMixin, FormMessagesMixin, FormView): d = Dispositivo.objects.get(pk=item) self.object.dispositivos_vigencias_set.add(d) return self.form_valid(form) - except: + except Exception as e: + username = request.user.username + self.logger.error("user=" + username + ". " + str(e)) return self.form_invalid(form) else: return self.form_invalid(form) @@ -3239,6 +3289,7 @@ class DispositivoEdicaoAlteracaoView(CompMixin, FormMessagesMixin, UpdateView): 'as alterações no Dispositivo') permission_required = 'compilacao.change_dispositivo_registros_compilacao' + logger = logging.getLogger(__name__) @property def cancel_url(self): @@ -3262,7 +3313,9 @@ class DispositivoEdicaoAlteracaoView(CompMixin, FormMessagesMixin, UpdateView): try: with transaction.atomic(): return self.form_valid(form) - except: + except Exception as e: + username = request.user.username + self.logger.error("user=" + username + ". " + str(e)) return self.form_invalid(form) else: return self.form_invalid(form) diff --git a/sapl/crud/base.py b/sapl/crud/base.py index 162668c76..e720b36cd 100644 --- a/sapl/crud/base.py +++ b/sapl/crud/base.py @@ -1,4 +1,4 @@ - +import logging from braces.views import FormMessagesMixin from compressor.utils.decorators import cached_property from crispy_forms.bootstrap import FieldWithButtons, StrictButton @@ -102,6 +102,7 @@ variáveis do crud: class SearchMixin(models.Model): search = models.TextField(blank=True, default='') + logger = logging.getLogger(__name__) class Meta: abstract = True @@ -116,7 +117,9 @@ class SearchMixin(models.Model): if len(fields) == 1: try: search += str(getattr(self, str_field)) + ' ' - except: + except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) pass else: _self = self @@ -379,6 +382,7 @@ class CrudBaseMixin(CrispyLayoutFormMixin): class CrudListView(PermissionRequiredContainerCrudMixin, ListView): permission_required = (RP_LIST, ) + logger = logging.getLogger(__name__) @classmethod def get_url_regex(cls): @@ -552,7 +556,9 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): fm = None try: fm = model._meta.get_field(fo) - except: + except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) pass if fm and hasattr(fm, 'related_model')\ @@ -597,6 +603,7 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): class CrudCreateView(PermissionRequiredContainerCrudMixin, FormMessagesMixin, CreateView): permission_required = (RP_ADD, ) + logger = logging.getLogger(__name__) @classmethod def get_url_regex(cls): @@ -621,7 +628,9 @@ class CrudCreateView(PermissionRequiredContainerCrudMixin, try: self.object.owner = self.request.user self.object.modifier = self.request.user - except: + except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) pass if self.container_field: @@ -668,6 +677,7 @@ class CrudDetailView(PermissionRequiredContainerCrudMixin, permission_required = (RP_DETAIL, ) no_entries_msg = _('Nenhum registro Associado.') paginate_by = 10 + logger = logging.getLogger(__name__) @classmethod def get_url_regex(cls): @@ -690,7 +700,9 @@ class CrudDetailView(PermissionRequiredContainerCrudMixin, self.object, obj.model_set).model._meta.get_field( fieldname).related_model._meta.verbose_name_plural) for fieldname in self.list_field_names_set] - except: + except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) obj = self.crud if hasattr(self, 'crud') else self return [getattr( self.object, @@ -717,7 +729,9 @@ class CrudDetailView(PermissionRequiredContainerCrudMixin, self.resolve_model_set_url(ACTION_DETAIL, args=(obj.id,)) if i == 0 else None) for i, name in enumerate(self.list_field_names_set)] - except: + except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) return [( getattr(obj, name), self.resolve_model_set_url(ACTION_DETAIL, args=(obj.id,)) @@ -732,7 +746,9 @@ class CrudDetailView(PermissionRequiredContainerCrudMixin, def get(self, request, *args, **kwargs): try: self.object = self.model.objects.get(pk=kwargs.get('pk')) - except: + except Exception as e: + username = request.user.username + self.logger.error("user=" + username + ". " + str(e)) raise Http404 obj = self.crud if hasattr(self, 'crud') else self if hasattr(obj, 'model_set') and obj.model_set: @@ -805,12 +821,16 @@ class CrudDetailView(PermissionRequiredContainerCrudMixin, class CrudUpdateView(PermissionRequiredContainerCrudMixin, FormMessagesMixin, UpdateView): permission_required = (RP_CHANGE, ) + logger = logging.getLogger(__name__) def form_valid(self, form): + self.object = form.instance try: self.object.modifier = self.request.user - except: + except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) pass return super().form_valid(form) @@ -832,6 +852,7 @@ class CrudUpdateView(PermissionRequiredContainerCrudMixin, class CrudDeleteView(PermissionRequiredContainerCrudMixin, FormMessagesMixin, DeleteView): permission_required = (RP_DELETE, ) + logger = logging.getLogger(__name__) @classmethod def get_url_regex(cls): @@ -853,12 +874,20 @@ class CrudDeleteView(PermissionRequiredContainerCrudMixin, error_msg = 'Registro não pode ser removido, pois\ é referenciado por outros registros:
\ ' - + + username = request.user.username + self.logger.error("user=" + username + ". Registro não pode ser removido, pois " + "é referenciado por outros registros: " + error_msg2) messages.add_message(request, messages.ERROR, error_msg) @@ -1062,12 +1091,14 @@ class MasterDetailCrud(Crud): class ListView(Crud.ListView): permission_required = RP_LIST, + logger = logging.getLogger(__name__) @classmethod def get_url_regex(cls): return r'^(?P\d+)/%s$' % cls.model._meta.model_name def get_context_data(self, **kwargs): + obj = self.crud if hasattr(self, 'crud') else self context = CrudListView.get_context_data(self, **kwargs) @@ -1098,7 +1129,9 @@ class MasterDetailCrud(Crud): try: parent_object = parent_model.objects.get(**params) - except: + except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) raise Http404() context[ @@ -1119,6 +1152,7 @@ class MasterDetailCrud(Crud): class CreateView(Crud.CreateView): permission_required = RP_ADD, + logger = logging.getLogger(__name__) def dispatch(self, request, *args, **kwargs): return PermissionRequiredMixin.dispatch( @@ -1144,6 +1178,7 @@ class MasterDetailCrud(Crud): return form def get_context_data(self, **kwargs): + obj = self.crud if hasattr(self, 'crud') else self context = Crud.CreateView.get_context_data( self, **kwargs) @@ -1161,7 +1196,9 @@ class MasterDetailCrud(Crud): try: parent_object = parent_model.objects.get(**params) - except Exception: + except Exception as e: + username = self.request.user.username + self.logger.error("user=" + username + ". " + str(e)) raise Http404() else: parent_model = self.model diff --git a/sapl/legacy/management/commands/ressucitar_deps.py b/sapl/legacy/management/commands/ressuscitar_deps.py similarity index 54% rename from sapl/legacy/management/commands/ressucitar_deps.py rename to sapl/legacy/management/commands/ressuscitar_deps.py index 70900e887..ea219330c 100644 --- a/sapl/legacy/management/commands/ressucitar_deps.py +++ b/sapl/legacy/management/commands/ressuscitar_deps.py @@ -1,12 +1,12 @@ from django.core.management.base import BaseCommand -from sapl.legacy.scripts.ressucita_dependencias import adiciona_ressucitar +from sapl.legacy.scripts.ressuscita_dependencias import adiciona_ressuscitar class Command(BaseCommand): - help = 'Ressucita dependências apagadas ' \ + help = 'Ressuscita dependências apagadas ' \ 'que são necessárias para migrar outros registros' def handle(self, *args, **options): - adiciona_ressucitar() + adiciona_ressuscitar() diff --git a/sapl/legacy/migracao.py b/sapl/legacy/migracao.py index c4c183b84..94fd25930 100644 --- a/sapl/legacy/migracao.py +++ b/sapl/legacy/migracao.py @@ -70,12 +70,13 @@ def scrap_sde(url, usuario, senha=None): {'__ac_name': usuario, '__ac_password': senha}) assert res.status_code == 200 - url_proposicao = '{}/sapl_documentos/proposicao/{}/renderXML?xsl=__default__' # noqa + url_proposicao_tmpl = '{}/sapl_documentos/proposicao/{}/renderXML?xsl=__default__' # noqa total = Proposicao.objects.count() for num, proposicao in enumerate(Proposicao.objects.all()): pk = proposicao.pk - res = session.get(url_proposicao.format(url, pk)) - print("pk: {} status: {} (progresso: {:.2%})".format( - pk, res.status_code, num / total)) + url_proposicao = url_proposicao_tmpl.format(url, pk) + res = session.get(url_proposicao) + print("pk: {} status: {} {} (progresso: {:.2%})".format( + pk, res.status_code, url_proposicao, num / total)) if res.status_code == 200: salva_conteudo_do_sde(proposicao, res.content) diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py index 1be1f6e96..1def7f9c3 100644 --- a/sapl/legacy/migracao_dados.py +++ b/sapl/legacy/migracao_dados.py @@ -623,6 +623,36 @@ def corrige_unidades_tramitacao_destino_vazia_como_anterior(): '''.format(tabela_tramitacao)) +def apaga_ref_a_mats_e_docs_inexistentes_em_proposicoes(): + # as referencias a matérias e documentos apagados não aparecem no 3.1 + # além do que, se ressuscitássemos essas matérias e docs, + # não seria possível apagá-los, + # pois é impossível para um usuário não autor acessar as proposicões + # para apagar a referências antes + exec_legado(''' + update proposicao set cod_materia = NULL where cod_materia not in ( + select cod_materia from materia_legislativa + where ind_excluido <> 1); + ''') + props_sem_mats = list(primeira_coluna(exec_legado(''' + select cod_proposicao from proposicao p inner join tipo_proposicao t + on p.tip_proposicao = t.tip_proposicao + where t.ind_mat_ou_doc = 'M' and cod_mat_ou_doc not in ( + select cod_materia from materia_legislativa + where ind_excluido <> 1) + '''))) + props_sem_docs = list(primeira_coluna(exec_legado(''' + select cod_proposicao from proposicao p inner join tipo_proposicao t + on p.tip_proposicao = t.tip_proposicao + where t.ind_mat_ou_doc = 'D' and cod_mat_ou_doc not in ( + select cod_documento from documento_acessorio + where ind_excluido <> 1); + '''))) + exec_legado_em_subconjunto(''' + update proposicao set cod_mat_ou_doc = NULL + where cod_proposicao in {}''', props_sem_mats + props_sem_docs) + + def uniformiza_banco(): propaga_exclusoes(PROPAGACOES_DE_EXCLUSAO) checa_registros_votacao_ambiguos_e_remove_nao_usados() @@ -722,6 +752,8 @@ sessao_plenaria_presenca | dat_sessao = NULL | dat_sessao = 0 select cod_materia from materia_legislativa where ind_excluido <> 1);''') + apaga_ref_a_mats_e_docs_inexistentes_em_proposicoes() + class Record: pass diff --git a/sapl/legacy/migracao_documentos.py b/sapl/legacy/migracao_documentos.py index 66ada19fc..2c9e00842 100644 --- a/sapl/legacy/migracao_documentos.py +++ b/sapl/legacy/migracao_documentos.py @@ -1,4 +1,5 @@ import os +import shutil import re from glob import glob from os.path import join @@ -52,6 +53,12 @@ def mover_documento(repo, origem, destino, ignora_origem_ausente=False): if ignora_origem_ausente and not os.path.exists(origem): print('Origem ignorada ao mover documento: {}'.format(origem)) return + # apaga destino, se houver, e renomeia origem para destino + if os.path.exists(destino): + if os.path.isdir(destino): + shutil.rmtree(destino) + else: + os.remove(destino) os.makedirs(os.path.dirname(destino), exist_ok=True) os.rename(origem, destino) diff --git a/sapl/legacy/scripts/ressucita_dependencias.py b/sapl/legacy/scripts/ressuscita_dependencias.py similarity index 96% rename from sapl/legacy/scripts/ressucita_dependencias.py rename to sapl/legacy/scripts/ressuscita_dependencias.py index 9810924a9..5e12efc01 100644 --- a/sapl/legacy/scripts/ressucita_dependencias.py +++ b/sapl/legacy/scripts/ressuscita_dependencias.py @@ -70,6 +70,7 @@ tipo_dependente /sistema/parlamentar/tipo-dependente origem /sistema/materia/origem documento_acessorio /materia/documentoacessorio tipo_fim_relatoria /sistema/materia/tipo-fim-relatoria +tipo_situacao_militar /sistema/parlamentar/tipo-militar ''' urls = dict(stripsplit(urls)) @@ -194,7 +195,7 @@ Para facilitar sua conferência, seguem os links para as proposições envolvida '''.format(table.draw(), links, sqls) -def get_dependencias_a_ressucitar(slug): +def get_dependencias_a_ressuscitar(slug): ocorrencias = yaml.load( Path(DIR_REPO.child('ocorrencias.yaml').read_file())) fks_faltando = ocorrencias.get('fk') @@ -265,7 +266,7 @@ SQLS_CRIACAO = [ ('unidade_tramitacao', ''' insert into unidade_tramitacao ( cod_unid_tramitacao, cod_comissao, cod_orgao, cod_parlamentar, ind_excluido) - values ({}, NULL, NULL, NULL, 0); + values ({}, NULL, NULL, 0, 0); '''), ('autor', SQL_INSERT_TIPO_AUTOR.format(0) + ''' insert into autor ( @@ -307,6 +308,9 @@ SQLS_CRIACAO = [ insert into parlamentar (cod_parlamentar, nom_completo, nom_parlamentar, sex_parlamentar, cod_casa, ind_ativo, ind_unid_deliberativa, ind_excluido) values ({}, "DESCONHECIDO", "DESCONHECIDO", "M", 0, 0, 0, 0); '''), + ('tipo_sessao_plenaria', ''' + insert into tipo_sessao_plenaria (tip_sessao, nom_sessao, ind_excluido, num_minimo) values ({}, "DESCONHECIDO", 0, 0); + '''), ] SQLS_CRIACAO = {k: (dedent(sql.strip()), extras) for k, sql, *extras in SQLS_CRIACAO} @@ -349,8 +353,8 @@ def get_sql_criar(tabela_alvo, campo, valor, slug): return sql, links -TEMPLATE_RESSUCITADOS = '''{} -/* RESSUCITADOS +TEMPLATE_RESSUSCITADOS = '''{} +/* RESSUSCITADOS SOBRE REGISTROS QUE ESTAVAM APAGADOS E FORAM RESTAURADOS @@ -397,11 +401,11 @@ def get_sqls_desexcluir_criar(preambulo, desexcluir, criar, slug): links = sem_repeticoes_mantendo_ordem(links) sqls, links = ['\n'.join(sorted(s)) for s in [sqls, links]] - return TEMPLATE_RESSUCITADOS.format(preambulo, links, sqls) + return TEMPLATE_RESSUSCITADOS.format(preambulo, links, sqls) -def get_ressucitar(slug): - preambulo, desexcluir, criar = get_dependencias_a_ressucitar(slug) +def get_ressuscitar(slug): + preambulo, desexcluir, criar = get_dependencias_a_ressuscitar(slug) return get_sqls_desexcluir_criar(preambulo, desexcluir, criar, slug) @@ -413,8 +417,8 @@ def get_slug(): return siglas_para_slugs[sigla] -def adiciona_ressucitar(): - sqls = get_ressucitar(get_slug()) +def adiciona_ressuscitar(): + sqls = get_ressuscitar(get_slug()) if sqls.strip(): arq_ajustes_pre_migracao = get_arquivo_ajustes_pre_migracao() conteudo = arq_ajustes_pre_migracao.read_file() diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 8fb26d084..c3ce1f36f 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -1,6 +1,6 @@ import os - +import logging import django_filters from crispy_forms.bootstrap import Alert, FormActions, InlineRadios from crispy_forms.helper import FormHelper @@ -39,7 +39,7 @@ from sapl.norma.models import (LegislacaoCitada, NormaJuridica, from sapl.parlamentares.models import Legislatura from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo from sapl.settings import MAX_DOC_UPLOAD_SIZE -from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, +from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, SEPARADOR_HASH_PROPOSICAO, ChoiceWithoutValidationField, MateriaPesquisaOrderingFilter, RangeWidgetOverride, autor_label, autor_modal, gerar_hash_arquivo, @@ -119,6 +119,8 @@ class ReceberProposicaoForm(Form): class MateriaSimplificadaForm(ModelForm): + logger = logging.getLogger(__name__) + class Meta: model = MateriaLegislativa fields = ['tipo', 'numero', 'ano', 'data_apresentacao', @@ -156,6 +158,8 @@ class MateriaSimplificadaForm(ModelForm): ano = cleaned_data['ano'] if data_apresentacao.year != ano: + self.logger.error("O ano da matéria ({}) é diferente" + " do ano na data de apresentação ({}).".format(ano, data_apresentacao.year)) raise ValidationError("O ano da matéria não pode ser " "diferente do ano na data de apresentação") @@ -164,6 +168,8 @@ class MateriaSimplificadaForm(ModelForm): class MateriaLegislativaForm(ModelForm): + logger = logging.getLogger(__name__) + tipo_autor = ModelChoiceField(label=_('Tipo Autor'), required=False, queryset=TipoAutor.objects.all(), @@ -204,6 +210,8 @@ class MateriaLegislativaForm(ModelForm): if protocolo: if not Protocolo.objects.filter(numero=protocolo,ano=ano).exists(): + self.logger.error("Protocolo %s/%s não" + " existe" % (protocolo, ano)) raise ValidationError(_('Protocolo %s/%s não' ' existe' % (protocolo, ano))) @@ -217,15 +225,23 @@ class MateriaLegislativaForm(ModelForm): ano=ano).exists() if exist_materia or exist_doc: + self.logger.error("Protocolo %s/%s ja possui" + " documento vinculado" + % (protocolo, ano)) raise ValidationError(_('Protocolo %s/%s ja possui' ' documento vinculado' % (protocolo, ano))) p = Protocolo.objects.get(numero=protocolo,ano=ano) if p.tipo_materia != cleaned_data['tipo']: + self.logger.error("Tipo do Protocolo ({}) deve ser o mesmo do Tipo Matéria ({})." + .format(cleaned_data['tipo'], p.tipo_materia)) raise ValidationError(_('Tipo do Protocolo deve ser o mesmo do Tipo Matéria')) if data_apresentacao.year != ano: + self.logger.error("O ano da matéria ({}) é diferente " + "do ano na data de apresentação ({})." + .format(ano, data_apresentacao.year)) raise ValidationError(_("O ano da matéria não pode ser " "diferente do ano na data de apresentação")) @@ -234,6 +250,9 @@ class MateriaLegislativaForm(ModelForm): if ano_origem_externa and data_origem_externa and \ ano_origem_externa != data_origem_externa.year: + self.logger.error("O ano de origem externa da matéria ({}) é " + " diferente do ano na data de origem externa ({})." + .format(ano_origem_externa, data_origem_externa)) raise ValidationError(_("O ano de origem externa da matéria não " "pode ser diferente do ano na data de " "origem externa")) @@ -260,6 +279,8 @@ class MateriaLegislativaForm(ModelForm): class UnidadeTramitacaoForm(ModelForm): + logger = logging.getLogger(__name__) + class Meta: model = UnidadeTramitacao fields = ['comissao', 'orgao', 'parlamentar'] @@ -278,6 +299,7 @@ class UnidadeTramitacaoForm(ModelForm): if len(cleaned_data) != 1: msg = _('Somente um campo deve ser preenchido!') + self.logger.error("Somente um campo deve ser preenchido!") raise ValidationError(msg) return cleaned_data @@ -329,6 +351,8 @@ class DocumentoAcessorioForm(ModelForm): class RelatoriaForm(ModelForm): + logger = logging.getLogger(__name__) + class Meta: model = Relatoria fields = ['data_designacao_relator', 'comissao', 'parlamentar', @@ -348,8 +372,12 @@ class RelatoriaForm(ModelForm): cleaned_data = self.cleaned_data try: + self.logger.debug("Tentando obter objeto Comissao.") comissao = Comissao.objects.get(id=self.initial['comissao']) - except ObjectDoesNotExist: + 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)) msg = _('A localização atual deve ser uma comissão.') raise ValidationError(msg) else: @@ -365,6 +393,8 @@ class TramitacaoForm(ModelForm): initial=False, label=_("Urgente?")) + logger = logging.getLogger(__name__) + class Meta: model = Tramitacao fields = ['data_tramitacao', @@ -407,11 +437,16 @@ class TramitacaoForm(ModelForm): if ultima_tramitacao: destino = ultima_tramitacao.unidade_tramitacao_destino if (destino != self.cleaned_data['unidade_tramitacao_local']): + self.logger.error("A origem da nova tramitação ({}) não é igual ao " + "destino da última adicionada ({})!" + .format(self.cleaned_data['unidade_tramitacao_local'], destino)) msg = _('A origem da nova tramitação deve ser igual ao ' 'destino da última adicionada!') raise ValidationError(msg) if cleaned_data['data_tramitacao'] > timezone.now().date(): + self.logger.error('A data de tramitação informada ({}) não é ' + + 'menor ou igual a data de hoje!'.format(cleaned_data['data_tramitacao'])) msg = _( 'A data de tramitação deve ser ' + 'menor ou igual a data de hoje!') @@ -421,18 +456,27 @@ class TramitacaoForm(ModelForm): data_tram_form < ultima_tramitacao.data_tramitacao): msg = _('A data da nova tramitação deve ser ' + 'maior que a data da última tramitação!') + self.logger.error("A data da nova tramitação ({}) deve ser " + "maior que a data da última tramitação ({})!" + .format(data_tram_form, ultima_tramitacao.data_tramitacao)) raise ValidationError(msg) if data_enc_form: if data_enc_form < data_tram_form: msg = _('A data de encaminhamento deve ser ' + 'maior que a data de tramitação!') + self.logger.error("A data de encaminhamento ({}) deve ser " + "maior que a data de tramitação! ({})" + .format(data_enc_form, data_tram_form)) raise ValidationError(msg) if data_prazo_form: if data_prazo_form < data_tram_form: msg = _('A data fim de prazo deve ser ' + 'maior que a data de tramitação!') + self.logger.error("A data fim de prazo ({}) deve ser " + + "maior que a data de tramitação ({})!" + .format(data_prazo_form, data_tram_form)) raise ValidationError(msg) return cleaned_data @@ -445,6 +489,8 @@ class TramitacaoUpdateForm(TramitacaoForm): data_tramitacao = forms.DateField(widget=forms.HiddenInput()) + logger = logging.getLogger(__name__) + class Meta: model = Tramitacao fields = ['data_tramitacao', @@ -479,6 +525,11 @@ class TramitacaoUpdateForm(TramitacaoForm): if ultima_tramitacao != self.instance: if self.cleaned_data['unidade_tramitacao_destino'] != \ self.instance.unidade_tramitacao_destino: + self.logger.error("Você não pode mudar a Unidade de Destino desta " + "tramitação para {}, pois irá conflitar com a Unidade " + "Local da tramitação seguinte ({})." + .format(self.cleaned_data['unidade_tramitacao_destino'], + self.instance.unidade_tramitacao_destino)) raise ValidationError( 'Você não pode mudar a Unidade de Destino desta ' 'tramitação, pois irá conflitar com a Unidade ' @@ -505,6 +556,8 @@ class LegislacaoCitadaForm(ModelForm): ano = forms.CharField(label='Ano', required=True) + logger = logging.getLogger(__name__) + class Meta: model = LegislacaoCitada fields = ['tipo', @@ -532,11 +585,16 @@ class LegislacaoCitadaForm(ModelForm): cleaned_data = self.cleaned_data try: + self.logger.debug("Tentando obter objeto NormalJuridica (numero={}, ano={}, tipo={})." + .format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) norma = NormaJuridica.objects.get( numero=cleaned_data['numero'], ano=cleaned_data['ano'], tipo=cleaned_data['tipo']) except ObjectDoesNotExist: + self.logger.error("A norma a ser inclusa (numero={}, ano={}, tipo={}) " + "não existe no cadastro de Normas." + .format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) msg = _('A norma a ser inclusa não existe no cadastro' ' de Normas.') raise ValidationError(msg) @@ -562,10 +620,12 @@ class LegislacaoCitadaForm(ModelForm): if not self.instance.id: if filtro_base.exists(): msg = _('Essa Legislação já foi cadastrada.') + self.logger.error("Essa Legislação já foi cadastrada.") raise ValidationError(msg) else: if filtro_base.exclude(id=self.instance.id).exists(): msg = _('Essa Legislação já foi cadastrada.') + self.logger.error("Essa Legislação já foi cadastrada.") raise ValidationError(msg) return cleaned_data @@ -579,6 +639,7 @@ class LegislacaoCitadaForm(ModelForm): class NumeracaoForm(ModelForm): + logger = logging.getLogger(__name__) class Meta: model = Numeracao fields = ['tipo_materia', @@ -593,6 +654,9 @@ class NumeracaoForm(ModelForm): return self.cleaned_data try: + self.logger.info("Tentando obter objeto MateriaLegislativa (numero={}, ano={}. tipo={})." + .format(self.cleaned_data['numero_materia'], + self.cleaned_data['ano_materia'], self.cleaned_data['tipo_materia'])) MateriaLegislativa.objects.get( numero=self.cleaned_data['numero_materia'], ano=self.cleaned_data['ano_materia'], @@ -600,6 +664,9 @@ class NumeracaoForm(ModelForm): except ObjectDoesNotExist: msg = _('A matéria a ser inclusa não existe no cadastro' ' de matérias legislativas.') + self.logger.error("A MateriaLegislativa a ser inclusa (numero={}, ano={}. tipo={}) não existe no cadastro de matérias legislativas." + .format(self.cleaned_data['numero_materia'], + self.cleaned_data['ano_materia'], self.cleaned_data['tipo_materia'])) raise ValidationError(msg) if Numeracao.objects.filter( @@ -609,6 +676,9 @@ class NumeracaoForm(ModelForm): numero_materia=self.cleaned_data['numero_materia'] ).exists(): msg = _('Essa numeração já foi cadastrada.') + self.logger.error("Essa numeração (materia={}, tipo_materia={}, ano_materia={}, numero_materia={}) " + "já foi cadastrada.".format(self.instance.materia, self.cleaned_data['tipo_materia'], + self.cleaned_data['ano_materia'], self.cleaned_data['numero_materia'])) raise ValidationError(msg) return self.cleaned_data @@ -616,6 +686,8 @@ class NumeracaoForm(ModelForm): class AnexadaForm(ModelForm): + logger = logging.getLogger(__name__) + tipo = forms.ModelChoiceField( label='Tipo', required=True, @@ -640,24 +712,30 @@ class AnexadaForm(ModelForm): cleaned_data = self.cleaned_data try: + self.logger.info("Tentando obter objeto MateriaLegislativa (numero={}, ano={}, tipo={})." + .format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) materia_anexada = MateriaLegislativa.objects.get( numero=cleaned_data['numero'], ano=cleaned_data['ano'], tipo=cleaned_data['tipo']) except ObjectDoesNotExist: - msg = _('A matéria a ser anexada não existe no cadastro' - ' de matérias legislativas.') + 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'])) + self.logger.error("A matéria a ser anexada não existe no cadastro" + " de matérias legislativas.") raise ValidationError(msg) materia_principal = self.instance.materia_principal if materia_principal == materia_anexada: + self.logger.error("Matéria não pode ser anexada a si mesma.") raise ValidationError(_('Matéria não pode ser anexada a si mesma')) is_anexada = Anexada.objects.filter(materia_principal=materia_principal, materia_anexada=materia_anexada ).exists() if is_anexada: - raise ValidationError(_('Materia já se encontra anexada')) + self.logger.error("Matéria já se encontra anexada.") + raise ValidationError(_('Matéria já se encontra anexada')) cleaned_data['materia_anexada'] = materia_anexada @@ -679,7 +757,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet): filter_overrides = {models.DateField: { 'filter_class': django_filters.DateFromToRangeFilter, 'extra': lambda f: { - 'label': '%s (%s)' % (f.verbose_name, _('Inicial - Final')), + 'label': '%s (%s)' % (f.verbose_name, _('Inicial Final')), 'widget': RangeWidgetOverride} }} @@ -858,6 +936,8 @@ class AutoriaForm(ModelForm): data_relativa = forms.DateField( widget=forms.HiddenInput(), required=False) + logger = logging.getLogger(__name__) + def __init__(self, *args, **kwargs): super(AutoriaForm, self).__init__(*args, **kwargs) @@ -889,6 +969,7 @@ class AutoriaForm(ModelForm): if ((not pk and autorias.exists()) or (pk and autorias.exclude(pk=pk).exists())): + self.logger.error("Esse Autor (pk={}) já foi cadastrado.".format(pk)) raise ValidationError(_('Esse Autor já foi cadastrado.')) return cd @@ -896,6 +977,8 @@ class AutoriaForm(ModelForm): class AutoriaMultiCreateForm(Form): + logger = logging.getLogger(__name__) + tipo_autor = ModelChoiceField(label=_('Tipo Autor'), required=False, queryset=TipoAutor.objects.all(), @@ -937,6 +1020,7 @@ class AutoriaMultiCreateForm(Form): del self.errors['autores'] if 'autor' not in cd or not cd['autor'].exists(): + self.logger.error("Ao menos um autor deve ser selecionado para inclusão") raise ValidationError( _('Ao menos um autor deve ser selecionado para inclusão')) @@ -1049,6 +1133,8 @@ class TramitacaoEmLoteFilterSet(django_filters.FilterSet): class TipoProposicaoForm(ModelForm): + logger = logging.getLogger(__name__) + content_type = forms.ModelChoiceField( queryset=ContentType.objects.all(), label=TipoProposicao._meta.get_field('content_type').verbose_name, @@ -1112,11 +1198,14 @@ class TipoProposicaoForm(ModelForm): if 'tipo_conteudo_related' not in cd or not cd[ 'tipo_conteudo_related']: + self.logger.error("Seleção de Tipo não definida.") raise ValidationError( - _('Seleção de Tipo não definida')) + _('Seleção de Tipo não definida.')) if not content_type.model_class().objects.filter( pk=cd['tipo_conteudo_related']).exists(): + self.logger.error("O Registro definido (%s) não está na base de %s." + % (cd['tipo_conteudo_related'], content_type)) raise ValidationError( _('O Registro definido (%s) não está na base de %s.' ) % (cd['tipo_conteudo_related'], content_type)) @@ -1180,7 +1269,7 @@ class TipoProposicaoSelect(Select): str(data_has_perfil), force_text(option_label)) - def render_options(self, choices, selected_choices): + def render_options(self, selected_choices): # Normalize to strings. selected_choices = set(force_text(v) for v in selected_choices) output = [] @@ -1200,6 +1289,8 @@ class TipoProposicaoSelect(Select): class ProposicaoForm(forms.ModelForm): + logger = logging.getLogger(__name__) + TIPO_TEXTO_CHOICE = [ ('D', _('Arquivo Digital')), ('T', _('Texto Articulado')) @@ -1320,6 +1411,7 @@ class ProposicaoForm(forms.ModelForm): texto_original = self.cleaned_data.get('texto_original', False) if texto_original and texto_original.size > MAX_DOC_UPLOAD_SIZE: max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) + self.logger.error("- Arquivo muito grande. ( > {0}MB )".format(max_size)) raise ValidationError( "Arquivo muito grande. ( > {0}MB )".format(max_size)) return texto_original @@ -1335,8 +1427,7 @@ class ProposicaoForm(forms.ModelForm): inst.texto_original.path, str(inst.pk)) elif inst.texto_articulado.exists(): ta = inst.texto_articulado.first() - # FIXME hash para textos articulados - inst.hash_code = 'P' + ta.hash() + '/' + str(inst.pk) + inst.hash_code = 'P' + ta.hash() + SEPARADOR_HASH_PROPOSICAO + str(inst.pk) def clean(self): super(ProposicaoForm, self).clean() @@ -1352,14 +1443,20 @@ class ProposicaoForm(forms.ModelForm): if tm and am and nm: try: + self.logger.debug("Tentando obter objeto MateriaLegislativa (tipo_id={}, ano={}, numero={})." + .format(tm, am, nm)) materia_de_vinculo = MateriaLegislativa.objects.get( tipo_id=tm, ano=am, numero=nm ) except ObjectDoesNotExist: + self.logger.error("Objeto MateriaLegislativa vinculada (tipo_id={}, ano={}, numero={}) não existe!" + .format(tm, am, nm)) raise ValidationError(_('Matéria Vinculada não existe!')) else: + self.logger.info("MateriaLegislativa vinculada (tipo_id={}, ano={}, numero={}) com sucesso." + .format(tm, am, nm)) cd['materia_de_vinculo'] = materia_de_vinculo return cd @@ -1406,6 +1503,8 @@ class DevolverProposicaoForm(forms.ModelForm): justificativa_devolucao = forms.CharField( required=False, widget=widgets.Textarea(attrs={'rows': 5})) + logger = logging.getLogger(__name__) + class Meta: model = Proposicao fields = [ @@ -1446,6 +1545,7 @@ class DevolverProposicaoForm(forms.ModelForm): if 'justificativa_devolucao' not in cd or\ not cd['justificativa_devolucao']: # TODO Implementar notificação ao autor por email + self.logger.error("Adicione uma Justificativa para devolução.") raise ValidationError( _('Adicione uma Justificativa para devolução.')) return cd @@ -1538,6 +1638,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): self.instance = kwargs.get('instance', None) if not self.instance: + self.logger.error("Erro na Busca por proposição a incorporar") raise ValueError(_('Erro na Busca por proposição a incorporar')) if self.instance.tipo.content_type.model_class() == TipoDocumento: @@ -1631,6 +1732,9 @@ class ConfirmarProposicaoForm(ProposicaoForm): numeracao = sapl.base.models.AppConfig.attr('sequencia_numeracao') if not numeracao: + self.logger.error("A sequência de numeração (por ano ou geral)" + " não foi configurada para a aplicação em " + "tabelas auxiliares") raise ValidationError("A sequência de numeração (por ano ou geral)" " não foi configurada para a aplicação em " "tabelas auxiliares") @@ -1641,12 +1745,14 @@ class ConfirmarProposicaoForm(ProposicaoForm): TipoMateriaLegislativa: if 'regime_tramitacao' not in cd or\ not cd['regime_tramitacao']: + self.logger.error("Regime de Tramitação deve ser informado.") raise ValidationError( _('Regime de Tramitação deve ser informado.')) elif self.instance.tipo.content_type.model_class( ) == TipoDocumento and not cd['materia_de_vinculo']: - + self.logger.error("Documentos não podem ser incorporados sem definir " + "para qual Matéria Legislativa ele se destina.") raise ValidationError( _('Documentos não podem ser incorporados sem definir ' 'para qual Matéria Legislativa ele se destina.')) @@ -1698,9 +1804,11 @@ class ConfirmarProposicaoForm(ProposicaoForm): numeracao = None try: + self.logger.debug("Tentando obter modelo de sequência de numeração.") numeracao = sapl.base.models.AppConfig.objects.last( ).sequencia_numeracao - except AttributeError: + except AttributeError as e: + self.logger.error("Erro ao obter modelo. " + str(e)) pass tipo = self.instance.tipo.tipo_conteudo_related @@ -1911,6 +2019,9 @@ class MateriaAssuntoForm(ModelForm): class EtiquetaPesquisaForm(forms.Form): + + logger = logging.getLogger(__name__) + tipo_materia = forms.ModelChoiceField( label=TipoMateriaLegislativa._meta.verbose_name, queryset=TipoMateriaLegislativa.objects.all(), @@ -1972,12 +2083,16 @@ class EtiquetaPesquisaForm(forms.Form): # preencheu o Final, ou vice-versa if (not cleaned_data['data_inicial'] or not cleaned_data['data_final']): + self.logger.error("Caso pesquise por data, os campos de Data Incial e " + "Data Final devem ser preenchidos obrigatoriamente") raise ValidationError(_( 'Caso pesquise por data, os campos de Data Incial e ' + 'Data Final devem ser preenchidos obrigatoriamente')) # Caso tenha preenchido, verifica se a data final é maior que # a inicial elif cleaned_data['data_final'] < cleaned_data['data_inicial']: + self.logger.error("A Data Final ({}) não pode ser menor que a Data Inicial({})." + .format(cleaned_data['data_final'], cleaned_data['data_inicial'])) raise ValidationError(_( 'A Data Final não pode ser menor que a Data Inicial')) @@ -1986,12 +2101,17 @@ class EtiquetaPesquisaForm(forms.Form): cleaned_data['processo_final']): if (not cleaned_data['processo_inicial'] or not cleaned_data['processo_final']): + self.logger.error("Caso pesquise por número de processo, os campos de " + "Processo Inicial e Processo Final " + "devem ser preenchidos obrigatoriamente") raise ValidationError(_( 'Caso pesquise por número de processo, os campos de ' + 'Processo Inicial e Processo Final ' + 'devem ser preenchidos obrigatoriamente')) elif (cleaned_data['processo_final'] < cleaned_data['processo_inicial']): + self.logger.error("O processo final ({}) não pode ser menor que o inicial ({})." + .format(cleaned_data['processo_final'], cleaned_data['processo_inicial'])) raise ValidationError(_( 'O processo final não pode ser menor que o inicial')) @@ -1999,6 +2119,9 @@ class EtiquetaPesquisaForm(forms.Form): class FichaPesquisaForm(forms.Form): + + logger = logging.getLogger(__name__) + tipo_materia = forms.ModelChoiceField( label=TipoMateriaLegislativa._meta.verbose_name, queryset=TipoMateriaLegislativa.objects.all(), @@ -2043,6 +2166,8 @@ class FichaPesquisaForm(forms.Form): return cleaned_data if cleaned_data['data_final'] < cleaned_data['data_inicial']: + self.logger.error("A Data Final ({}) não pode ser menor que a Data Inicial ({})." + .format(cleaned_data['data_final'], cleaned_data['data_inicial'])) raise ValidationError(_( 'A Data Final não pode ser menor que a Data Inicial')) @@ -2073,6 +2198,8 @@ class FichaSelecionaForm(forms.Form): class ExcluirTramitacaoEmLote(forms.Form): + logger = logging.getLogger(__name__) + data_tramitacao = forms.DateField(required=True, label=_('Data da Tramitação')) @@ -2109,6 +2236,11 @@ class ExcluirTramitacaoEmLote(forms.Form): unidade_tramitacao_destino=unidade_tramitacao_destino, status=status) if not tramitacao_set.exists(): + self.logger.error("Não existem tramitações com os dados informados " + " (data_tramitacao={}, unidade_tramitacao_local={})." + "unidade_tramitacao_destino={}, status={})." + .format(data_tramitacao, unidade_tramitacao_local, + unidade_tramitacao_destino, status)) raise forms.ValidationError( _("Não existem tramitações com os dados informados.")) diff --git a/sapl/materia/migrations/0032_auto_20181022_1743.py b/sapl/materia/migrations/0032_auto_20181022_1743.py new file mode 100644 index 000000000..8721e831b --- /dev/null +++ b/sapl/materia/migrations/0032_auto_20181022_1743.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2018-10-22 20:43 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0031_auto_20180924_1724'), + ] + + operations = [ + migrations.AlterField( + model_name='autoria', + name='autor', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='base.Autor', verbose_name='Autor'), + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index 9e78092e5..b3dfc1671 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -308,7 +308,7 @@ class MateriaLegislativa(models.Model): class Autoria(models.Model): autor = models.ForeignKey(Autor, verbose_name=_('Autor'), - on_delete=models.CASCADE) + on_delete=models.PROTECT) materia = models.ForeignKey( MateriaLegislativa, on_delete=models.CASCADE, verbose_name=_('Matéria Legislativa')) diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 9b10606a1..d38d73d50 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -7,7 +7,7 @@ from crispy_forms.layout import HTML from django.contrib import messages from django.contrib.auth.decorators import permission_required from django.contrib.auth.mixins import PermissionRequiredMixin -from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.core.urlresolvers import reverse from django.db.models import Max from django.http import HttpResponse, JsonResponse @@ -21,6 +21,7 @@ from django.views.generic.base import RedirectView from django.views.generic.edit import FormView from django_filters.views import FilterView import weasyprint +import logging import sapl from sapl.base.models import Autor, CasaLegislativa @@ -41,7 +42,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.utils import (YES_NO_CHOICES, autor_label, autor_modal, +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, show_results_filter_set) @@ -92,11 +93,16 @@ def autores_ja_adicionados(materia_pk): def proposicao_texto(request, pk): + logger = logging.getLogger(__name__) + username = request.user.username.replace("'","") + logger.debug('user=' + username + '. Tentando obter objeto Proposicao com pk = {}.'.format(pk)) proposicao = Proposicao.objects.get(pk=pk) if proposicao.texto_original: if (not proposicao.data_recebimento and proposicao.autor.user_id != request.user.id): + logger.error("user=" + username + ". Usuário ({}) não tem permissão para acessar o texto original." + .format(request.user.id)) messages.error(request, _( 'Você não tem permissão para acessar o texto original.')) return redirect(reverse('sapl.materia:proposicao_detail', @@ -113,6 +119,7 @@ def proposicao_texto(request, pk): response['Content-Disposition'] = ( 'inline; filename="%s"' % arquivo.name.split('/')[-1]) return response + logger.error('user=' + username + '. Objeto Proposicao com pk={} não encontrado.'.format(pk)) raise Http404 @@ -165,6 +172,7 @@ class CriarProtocoloMateriaView(CreateView): template_name = "crud/form.html" form_class = MateriaSimplificadaForm form_valid_message = _('Matéria cadastrada com sucesso!') + logger = logging.getLogger(__name__) def get_success_url(self, materia): return reverse('sapl.materia:materialegislativa_detail', kwargs={ @@ -173,19 +181,25 @@ class CriarProtocoloMateriaView(CreateView): def get_context_data(self, **kwargs): context = super( CriarProtocoloMateriaView, self).get_context_data(**kwargs) + username = self.request.user.username.replace("'","") try: + self.logger.debug("user=" + username + ". Tentando obter objeto Protocolo.") protocolo = Protocolo.objects.get(pk=self.kwargs['pk']) - except ObjectDoesNotExist: + except ObjectDoesNotExist as e: + self.logger.error("user=" + username + ". Objeto Protocolo com pk={} não encontrado. ".format(self.kwargs['pk']) + str(e)) raise Http404() numero = 1 try: + self.logger.debug("user=" + username + ". Tentando obter materias do último ano.") materias_ano = MateriaLegislativa.objects.filter( ano=protocolo.ano, tipo=protocolo.tipo_materia).latest('numero') numero = materias_ano.numero + 1 except ObjectDoesNotExist: + self.logger.error("user=" + username + ". Não foram encontradas matérias no último ano ({}). " + "Definido 1 como padrão.".format(protocolo.ano)) pass # numero ficou com o valor padrão 1 acima context['form'].fields['tipo'].initial = protocolo.tipo_materia @@ -199,10 +213,13 @@ class CriarProtocoloMateriaView(CreateView): def form_valid(self, form): materia = form.save() + username = self.request.user.username.replace("'","") try: + self.logger.info("user=" + username + ". Tentando obter objeto Procolo com pk={}.".format(self.kwargs['pk'])) protocolo = Protocolo.objects.get(pk=self.kwargs['pk']) except ObjectDoesNotExist: + self.logger.error('user=' + username + '. Objeto Protocolo com pk={} não encontrado.'.format(self.kwargs['pk'])) raise Http404() if protocolo.autor: @@ -286,14 +303,18 @@ class ProposicaoTaView(IntegracaoTaView): @permission_required('materia.detail_materialegislativa') def recuperar_materia(request): + logger = logging.getLogger(__name__) + username = request.user.username.replace("'","") tipo = TipoMateriaLegislativa.objects.get(pk=request.GET['tipo']) ano = request.GET.get('ano', '') numeracao = None try: + logger.debug("user=" + username + ". Tentando obter numeração da matéria.") numeracao = sapl.base.models.AppConfig.objects.last( ).sequencia_numeracao - except AttributeError: + except AttributeError as e: + logger.error("user=" + username + ". " + str(e) + " Numeracao da matéria definida como None.") pass if tipo.sequencia_numeracao: @@ -481,27 +502,36 @@ class ReceberProposicao(PermissionRequiredForAppCrudMixin, FormView): form = ReceberProposicaoForm(request.POST) if form.is_valid(): - proposicoes = Proposicao.objects.filter( - data_envio__isnull=False, data_recebimento__isnull=True) + try: + # A ultima parte do código deve ser a pk da Proposicao + cod_hash = form.cleaned_data["cod_hash"]. \ + replace('/', SEPARADOR_HASH_PROPOSICAO) + id = cod_hash.split(SEPARADOR_HASH_PROPOSICAO)[1] + proposicao = Proposicao.objects.get(id=id, + data_envio__isnull=False, + data_recebimento__isnull=True) - for proposicao in proposicoes: if proposicao.texto_articulado.exists(): ta = proposicao.texto_articulado.first() # FIXME hash para textos articulados - hasher = 'P' + ta.hash() + '/' + str(proposicao.id) + hasher = 'P' + ta.hash() + SEPARADOR_HASH_PROPOSICAO + str(proposicao.id) else: hasher = gerar_hash_arquivo( proposicao.texto_original.path, - str(proposicao.pk)) \ + str(proposicao.id)) \ if proposicao.texto_original else None - if hasher == form.cleaned_data['cod_hash']: + if hasher == cod_hash: return HttpResponseRedirect( reverse('sapl.materia:proposicao-confirmar', kwargs={ - 'hash': hasher.split('/')[0][1:], + 'hash': hasher.split(SEPARADOR_HASH_PROPOSICAO)[0][1:], 'pk': proposicao.pk})) - - messages.error(request, _('Proposição não encontrada!')) + except ObjectDoesNotExist: + messages.error(request, _('Proposição não encontrada!')) + except IndexError: + messages.error(request, _('Código de recibo mal formado!')) + except IOError: + messages.error(request, _('Erro abrindo texto original de proposição')) return self.form_invalid(form) def get_success_url(self): @@ -519,20 +549,24 @@ class RetornarProposicao(UpdateView): model = Proposicao fields = ['data_envio', 'descricao' ] permission_required = ('materia.detail_proposicao_enviada', ) + logger = logging.getLogger(__name__) def dispatch(self, request, *args, **kwargs): - + username = request.user.username.replace("'","") try: + self.logger.info("user=" + username + ". Tentando obter objeto Proposicao com id={}.".format(kwargs['pk'])) p = Proposicao.objects.get(id=kwargs['pk']) except: + self.logger.error("user=" + username + ". Objeto Proposicao com id={} não encontrado.".format(kwargs['pk'])) raise Http404() if p.autor.user != request.user: - messages.error( + self.logger.error("user=" + username + ". Usuário ({}) sem acesso a esta opção.".format(request.user)) + messages.error( request, 'Usuário sem acesso a esta opção.' % request.user) - return redirect('/') + return redirect('/') return super(RetornarProposicao, self).dispatch( request, *args, **kwargs) @@ -543,6 +577,7 @@ class ConfirmarProposicao(PermissionRequiredForAppCrudMixin, UpdateView): template_name = "materia/confirmar_proposicao.html" model = Proposicao form_class = ConfirmarProposicaoForm, DevolverProposicaoForm + logger = logging.getLogger(__name__) def get_success_url(self): msgs = self.object.results['messages'] @@ -554,12 +589,15 @@ class ConfirmarProposicao(PermissionRequiredForAppCrudMixin, UpdateView): return self.object.results['url'] def get_object(self, queryset=None): + username = self.request.user.username.replace("'","") + try: """ Não deve haver acesso na rotina de confirmação a proposições: já recebidas -> data_recebimento != None não enviadas -> data_envio == None """ + self.logger.debug("user=" + username + ". Tentando obter objeto Proposicao.") proposicao = Proposicao.objects.get(pk=self.kwargs['pk'], data_envio__isnull=False, data_recebimento__isnull=True) @@ -567,18 +605,21 @@ class ConfirmarProposicao(PermissionRequiredForAppCrudMixin, UpdateView): if proposicao.texto_articulado.exists(): ta = proposicao.texto_articulado.first() - hasher = 'P' + ta.hash() + '/' + str(proposicao.id) + hasher = 'P' + ta.hash() + SEPARADOR_HASH_PROPOSICAO + str(proposicao.id) else: hasher = gerar_hash_arquivo( proposicao.texto_original.path, str(proposicao.pk)) if proposicao.texto_original else None - if hasher == 'P%s/%s' % (self.kwargs['hash'], proposicao.pk): + if hasher == 'P%s%s%s' % (self.kwargs['hash'], SEPARADOR_HASH_PROPOSICAO, proposicao.pk): self.object = proposicao - except: + except Exception as e: + self.logger.error("user=" + username + ". Objeto Proposicao com atributos (pk={}, data_envio=Not Null, " + "data_recebimento=Null) não encontrado. ".format(self.kwargs['pk']) + str(e)) raise Http404() if not self.object: + self.logger.error("user=" + username + ". Objeto vazio.") raise Http404() return self.object @@ -694,6 +735,7 @@ class ProposicaoCrud(Crud): 'materia.detail_proposicao_devolvida', 'materia.detail_proposicao_incorporada') + logger = logging.getLogger(__name__) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['subnav_template_name'] = '' @@ -704,7 +746,9 @@ class ProposicaoCrud(Crud): return context def get(self, request, *args, **kwargs): + action = request.GET.get('action', '') + username = request.user.username.replace("'","") if not action: return Crud.DetailView.get(self, request, *args, **kwargs) @@ -736,23 +780,32 @@ class ProposicaoCrud(Crud): messages.success(request, _( 'Proposição enviada com sucesso.')) try: + self.logger.debug("user=" + username + ". Tentando obter número do objeto MateriaLegislativa com " + "atributos tipo={} e ano={}." + .format(p.tipo.tipo_conteudo_related, p.ano)) numero = MateriaLegislativa.objects.filter(tipo=p.tipo.tipo_conteudo_related, ano=p.ano).last().numero + 1 messages.success(request, _( '%s : nº %s de %s
Atenção! Este número é apenas um provável ' 'número que pode não corresponder com a realidade' % (p.tipo, numero, p.ano))) - except ValueError: + except ValueError as e: + self.logger.error("user=" + username + "." + str(e)) pass - except AttributeError: + except AttributeError as e: + self.logger.error("user=" + username + "." + str(e)) pass - except TypeError: + except TypeError as e: + self.logger.error("user=" + username + "." + str(e)) pass elif action == 'return': if not p.data_envio: + self.logger.error("user=" + username + ". Proposição (numero={}) ainda não foi enviada.".format(p.numero_proposicao)) msg_error = _('Proposição ainda não foi enviada.') elif p.data_recebimento: + self.logger.error("user=" + username + ". Proposição (numero={}) já foi recebida, não é " + "possível retorná-la.".format(p.numero_proposicao)) msg_error = _('Proposição já foi recebida, não é ' 'possível retorná-la.') else: @@ -763,6 +816,7 @@ class ProposicaoCrud(Crud): ta.privacidade = STATUS_TA_PRIVATE ta.editing_locked = False ta.save() + self.logger.info("user=" + username + ". Proposição (numero={}) Retornada com sucesso.".format(p.numero_proposicao)) messages.success(request, _( 'Proposição Retornada com sucesso.')) @@ -774,10 +828,12 @@ class ProposicaoCrud(Crud): kwargs={'pk': kwargs['pk']})) def dispatch(self, request, *args, **kwargs): - + username = request.user.username.replace("'","") try: + self.logger.debug("user=" + username + ". Tentando obter objeto Proposicao com pk={}".format(kwargs['pk'])) p = Proposicao.objects.get(id=kwargs['pk']) - except: + except Exception as e: + self.logger.error("user=" + username + ". Erro ao obter proposicao com pk={}. Retornando 404. ".format(kwargs['pk']) + str(e)) raise Http404() if not self.has_permission(): @@ -806,16 +862,25 @@ class ProposicaoCrud(Crud): class DeleteView(BaseLocalMixin, Crud.DeleteView): + logger = logging.getLogger(__name__) + def _action_is_valid(self, request, *args, **kwargs): proposicao = Proposicao.objects.filter( id=kwargs['pk']).values_list( 'data_envio', 'data_recebimento') + username = request.user.username.replace("'","") + if proposicao: if proposicao[0][0] and proposicao[0][1]: + self.logger.error("user=" + username + ". Proposição (id={}) já foi enviada e recebida." + "Não pode mais ser excluida.".format(kwargs['pk'])) msg = _('Proposição já foi enviada e recebida.' 'Não pode mais ser excluida.') elif proposicao[0][0] and not proposicao[0][1]: + self.logger.error("user=" + username + ". Proposição (id={}) já foi enviada mas ainda não recebida " + "pelo protocolo. Use a opção Recuperar Proposição " + "para depois excluí-la.".format(kwargs['pk'])) msg = _('Proposição já foi enviada mas ainda não recebida ' 'pelo protocolo. Use a opção Recuperar Proposição ' 'para depois excluí-la.') @@ -827,17 +892,26 @@ class ProposicaoCrud(Crud): class UpdateView(BaseLocalMixin, Crud.UpdateView): + logger = logging.getLogger(__name__) + def _action_is_valid(self, request, *args, **kwargs): proposicao = Proposicao.objects.filter( id=kwargs['pk']).values_list( 'data_envio', 'data_recebimento') + + username = request.user.username.replace("'","") if proposicao: if proposicao[0][0] and proposicao[0][1]: + self.logger.error('user=' + username + '. Proposição (id={}) já foi enviada e recebida.' + 'Não pode mais ser editada'.format(kwargs['pk'])) msg = _('Proposição já foi enviada e recebida.' 'Não pode mais ser editada') elif proposicao[0][0] and not proposicao[0][1]: + self.logger.error('user=' + username + '. Proposição (id={}) já foi enviada mas ainda não recebida ' + 'pelo protocolo. Use a opção Recuperar Proposição ' + 'para voltar para edição.'.format(kwargs['pk'])) msg = _('Proposição já foi enviada mas ainda não recebida ' 'pelo protocolo. Use a opção Recuperar Proposição ' 'para voltar para edição.') @@ -850,6 +924,7 @@ class ProposicaoCrud(Crud): def get_success_url(self): tipo_texto = self.request.POST.get('tipo_texto', '') + username = self.request.user.username.replace("'","") if tipo_texto == 'T': messages.info(self.request, @@ -857,12 +932,17 @@ class ProposicaoCrud(Crud): 'alterada e a opção "Texto Articulado " for ' 'marcada, você será redirecionado para a ' 'edição do Texto Eletrônico.')) + self.logger.debug('user=' + username + '. Sempre que uma Proposição é inclusa ou ' + 'alterada e a opção "Texto Articulado " for ' + 'marcada, você será redirecionado para a ' + 'edição do Texto Eletrônico.') return reverse('sapl.materia:proposicao_ta', kwargs={'pk': self.object.pk}) else: return Crud.UpdateView.get_success_url(self) class CreateView(Crud.CreateView): + logger = logging.getLogger(__name__) form_class = ProposicaoForm layout_key = None @@ -874,6 +954,7 @@ class ProposicaoCrud(Crud): def get_success_url(self): tipo_texto = self.request.POST.get('tipo_texto', '') + username = self.request.user.username.replace("'","") if tipo_texto == 'T': messages.info(self.request, @@ -882,6 +963,11 @@ class ProposicaoCrud(Crud): 'marcada, você será redirecionado para o ' 'Texto Eletrônico. Use a opção "Editar Texto" ' 'para construir seu texto.')) + self.logger.debug('user=' + username + '. Sempre que uma Proposição é inclusa ou ' + 'alterada e a opção "Texto Articulado " for ' + 'marcada, você será redirecionado para o ' + 'Texto Eletrônico. Use a opção "Editar Texto" ' + 'para construir seu texto.') return reverse('sapl.materia:proposicao_ta', kwargs={'pk': self.object.pk}) else: @@ -913,6 +999,7 @@ class ProposicaoCrud(Crud): class ReciboProposicaoView(TemplateView): + logger = logging.getLogger(__name__) template_name = "materia/recibo_proposicao.html" permission_required = ('materia.detail_proposicao', ) @@ -950,14 +1037,18 @@ class ReciboProposicaoView(TemplateView): def get(self, request, *args, **kwargs): proposicao = Proposicao.objects.get(pk=self.kwargs['pk']) + username = request.user.username.replace("'","") if proposicao.data_envio: return TemplateView.get(self, request, *args, **kwargs) if not proposicao.data_envio and not proposicao.data_devolucao: + self.logger.error('user=' + username + '. Não é possível gerar recibo para uma ' + 'Proposição (pk={}) ainda não enviada.'.format(self.kwargs['pk'])) messages.error(request, _('Não é possível gerar recibo para uma ' 'Proposição ainda não enviada.')) elif proposicao.data_devolucao: + self.logger.error("user=" + username + ". Não é possível gerar recibo para proposicao de pk={}.".format(self.kwargs['pk'])) messages.error(request, _('Não é possível gerar recibo.')) return redirect(reverse('sapl.materia:proposicao_detail', @@ -972,17 +1063,22 @@ class RelatoriaCrud(MasterDetailCrud): class CreateView(MasterDetailCrud.CreateView): form_class = RelatoriaForm + logger = logging.getLogger(__name__) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + username = self.request.user.username.replace("'","") 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'])) composicao = comissao.composicao_set.order_by( '-periodo__data_inicio').first() participacao = Participacao.objects.filter( @@ -1018,15 +1114,22 @@ class RelatoriaCrud(MasterDetailCrud): class UpdateView(MasterDetailCrud.UpdateView): form_class = RelatoriaForm + logger = logging.getLogger(__name__) + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + username = self.request.user.username.replace("'","") 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: + except ObjectDoesNotExist: + self.logger.error("user=" + username + ". Objeto Comissão de pk={} não encontrado.".format(context['form'].initial['comissao'])) pass - else: + else: + self.logger.info("user=" + username + ". Objeto Comissao de pk={} obtido com sucesso.".format(context['form'].initial['comissao'])) composicao = comissao.composicao_set.order_by( '-periodo__data_inicio').first() participacao = Participacao.objects.filter( @@ -1054,6 +1157,7 @@ class TramitacaoCrud(MasterDetailCrud): class CreateView(MasterDetailCrud.CreateView): form_class = TramitacaoForm + logger = logging.getLogger(__name__) def get_success_url(self): return reverse('sapl.materia:tramitacao_list', kwargs={ @@ -1076,6 +1180,7 @@ class TramitacaoCrud(MasterDetailCrud): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + username = self.request.user.username.replace("'","") ultima_tramitacao = Tramitacao.objects.filter( materia_id=self.kwargs['pk']).order_by( @@ -1090,6 +1195,8 @@ class TramitacaoCrud(MasterDetailCrud): (ultima_tramitacao.unidade_tramitacao_destino.pk, ultima_tramitacao.unidade_tramitacao_destino)] else: + self.logger.error('user=' + username + '. Unidade de tramitação destino ' + 'da última tramitação não pode ser vazia!') msg = _('Unidade de tramitação destino ' ' da última tramitação não pode ser vazia!') messages.add_message(self.request, messages.ERROR, msg) @@ -1097,7 +1204,9 @@ class TramitacaoCrud(MasterDetailCrud): return context def form_valid(self, form): + self.object = form.save() + username = self.request.user.username.replace("'","") if form.instance.status.indicador == 'F': form.instance.materia.em_tramitacao = False @@ -1106,25 +1215,32 @@ class TramitacaoCrud(MasterDetailCrud): form.instance.materia.save() try: + self.logger.debug("user=" + username + ". Tentando enviar Tramitacao (sender={}, post={}, request={})." + .format(Tramitacao, self.object, self.request)) tramitacao_signal.send(sender=Tramitacao, post=self.object, request=self.request) - except Exception: + except Exception as e: # TODO log error msg = _('Tramitação criada, mas e-mail de acompanhamento ' 'de matéria não enviado. Há problemas na configuração ' 'do e-mail.') + self.logger.warning('user=' + username + '. Tramitação criada, mas e-mail de acompanhamento ' + 'de matéria não enviado. Há problemas na configuração ' + 'do e-mail. ' + str(e)) messages.add_message(self.request, messages.WARNING, msg) return HttpResponseRedirect(self.get_success_url()) return super().form_valid(form) class UpdateView(MasterDetailCrud.UpdateView): form_class = TramitacaoUpdateForm + logger = logging.getLogger(__name__) layout_key = 'TramitacaoUpdate' def form_valid(self, form): self.object = form.save() + username = self.request.user.username.replace("'","") if form.instance.status.indicador == 'F': form.instance.materia.em_tramitacao = False @@ -1133,6 +1249,8 @@ class TramitacaoCrud(MasterDetailCrud): form.instance.materia.save() try: + self.logger.debug("user=" + username + ". Tentando enviar Tramitacao (sender={}, post={}, request={}" + .format(Tramitacao, self.object, self.request)) tramitacao_signal.send(sender=Tramitacao, post=self.object, request=self.request) @@ -1141,6 +1259,9 @@ class TramitacaoCrud(MasterDetailCrud): msg = _('Tramitação atualizada, mas e-mail de acompanhamento ' 'de matéria não enviado. Há problemas na configuração ' 'do e-mail.') + self.logger.warning('user=' + username + '. Tramitação atualizada, mas e-mail de acompanhamento ' + 'de matéria não enviado. Há problemas na configuração ' + 'do e-mail.') messages.add_message(self.request, messages.WARNING, msg) return HttpResponseRedirect(self.get_success_url()) return super().form_valid(form) @@ -1156,6 +1277,8 @@ class TramitacaoCrud(MasterDetailCrud): class DeleteView(MasterDetailCrud.DeleteView): + logger = logging.getLogger(__name__) + def delete(self, request, *args, **kwargs): tramitacao = Tramitacao.objects.get(id=self.kwargs['pk']) materia = MateriaLegislativa.objects.get(id=tramitacao.materia.id) @@ -1166,8 +1289,13 @@ class TramitacaoCrud(MasterDetailCrud): '-data_tramitacao', '-timestamp', '-id').first() + + username = request.user.username.replace("'","") if tramitacao.pk != ultima_tramitacao.pk: + self.logger.error("user=" + username + ". Não é possível deletar a tramitação de pk={}. " + "Somente a última tramitação (pk={}) pode ser deletada!." + .format(tramitacao.pk, ultima_tramitacao.pk)) msg = _('Somente a última tramitação pode ser deletada!') messages.add_message(request, messages.ERROR, msg) return HttpResponseRedirect(url) @@ -1508,7 +1636,11 @@ class DocumentoAcessorioView(PermissionRequiredMixin, CreateView): class AcompanhamentoConfirmarView(TemplateView): + logger = logging.getLogger(__name__) + def get_redirect_url(self, email): + username = self.request.user.username.replace("'","") + self.logger.debug('user=' + username + '. Esta matéria está sendo acompanhada pelo e-mail: %s' % (email)) msg = _('Esta matéria está sendo acompanhada pelo e-mail: %s') % ( email) messages.add_message(self.request, messages.SUCCESS, msg) @@ -1518,18 +1650,24 @@ class AcompanhamentoConfirmarView(TemplateView): def get(self, request, *args, **kwargs): materia_id = kwargs['pk'] hash_txt = request.GET.get('hash_txt', '') + username = self.request.user.username.replace("'","") try: + self.logger.info("user=" + username + ". Tentando obter objeto AcompanhamentoMateria (materia_id={}, hash={})." + .format(materia_id, hash_txt)) acompanhar = AcompanhamentoMateria.objects.get( materia_id=materia_id, hash=hash_txt) except ObjectDoesNotExist: + self.logger.error("user=" + username + ". Objeto AcompanhamentoMateria(materia_id={}, hash={}) não encontrado." + .format(materia_id, hash_txt)) raise Http404() - # except MultipleObjectsReturned: + except MultipleObjectsReturned as e: # A melhor solução deve ser permitir que a exceção # (MultipleObjectsReturned) seja lançada e vá para o log, # pois só poderá ser causada por um erro de desenvolvimente - + self.logger.error('user=' + username + '.' + str(e)) + pass acompanhar.confirmado = True acompanhar.save() @@ -1538,7 +1676,11 @@ class AcompanhamentoConfirmarView(TemplateView): class AcompanhamentoExcluirView(TemplateView): + logger = logging.getLogger(__name__) + def get_success_url(self): + username = self.request.user.username.replace("'","") + self.logger.debug("user=" + username + ". Você parou de acompanhar esta matéria.") msg = _('Você parou de acompanhar esta matéria.') messages.add_message(self.request, messages.INFO, msg) return reverse('sapl.materia:materialegislativa_detail', @@ -1547,11 +1689,16 @@ class AcompanhamentoExcluirView(TemplateView): def get(self, request, *args, **kwargs): materia_id = kwargs['pk'] hash_txt = request.GET.get('hash_txt', '') + username = self.request.user.username.replace("'","") try: + self.logger.info("user=" + username + ". Tentando deletar objeto AcompanhamentoMateria (materia_id={}, hash={})." + .format(materia_id, hash_txt)) AcompanhamentoMateria.objects.get(materia_id=materia_id, hash=hash_txt).delete() except ObjectDoesNotExist: + self.logger.error("user=" + username + ". Objeto AcompanhamentoMateria (materia_id={}, hash={}) não encontrado." + .format(materia_id, hash_txt)) pass return HttpResponseRedirect(self.get_success_url()) @@ -1635,6 +1782,7 @@ class MateriaLegislativaPesquisaView(FilterView): class AcompanhamentoMateriaView(CreateView): + logger = logging.getLogger(__name__) template_name = "materia/acompanhamento_materia.html" def get_random_chars(self): @@ -1686,7 +1834,9 @@ class AcompanhamentoMateriaView(CreateView): "materia", materia, destinatario) - + self.logger.debug('user=' + usuario.username + '. Foi enviado um e-mail de confirmação. Confira sua caixa \ + de mensagens e clique no link que nós enviamos para \ + confirmar o acompanhamento desta matéria.') msg = _('Foi enviado um e-mail de confirmação. Confira sua caixa \ de mensagens e clique no link que nós enviamos para \ confirmar o acompanhamento desta matéria.') @@ -1695,6 +1845,7 @@ class AcompanhamentoMateriaView(CreateView): # Caso esse Acompanhamento já exista # avisa ao usuário que essa matéria já está sendo acompanhada else: + self.logger.debug("user=" + usuario.username + ". Este e-mail já está acompanhando essa matéria.") msg = _('Este e-mail já está acompanhando essa matéria.') messages.add_message(request, messages.INFO, msg) @@ -1773,6 +1924,8 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): primeira_tramitacao = True + logger = logging.getLogger(__name__) + def get_context_data(self, **kwargs): context = super(PrimeiraTramitacaoEmLoteView, self).get_context_data(**kwargs) @@ -1815,10 +1968,13 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): def post(self, request, *args, **kwargs): + marcadas = request.POST.getlist('materia_id') tz = timezone.get_current_timezone() + username = request.user.username.replace("'","") + if len(marcadas) == 0: msg = _('Nenhuma máteria foi selecionada.') messages.add_message(request, messages.ERROR, msg) @@ -1870,10 +2026,15 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): ) t.save() try: + self.logger.debug("user=" + username + ". Tentando enviar tramitação.") tramitacao_signal.send(sender=Tramitacao, post=t, request=self.request) - except Exception: + + except Exception as e: + self.logger.error('user=' + username + '. Tramitação criada , mas e-mail de acompanhamento ' + 'de matéria não enviado. Há problemas na configuração ' + 'do e-mail. ' + str(e)) flag_error = True if flag_error: msg = _('Tramitação criada, mas e-mail de acompanhamento ' @@ -1891,6 +2052,7 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): materia.save() msg = _('Tramitação completa.') + self.logger.info('user=' + username + '. Tramitação completa.') messages.add_message(request, messages.SUCCESS, msg) return self.get(request, self.kwargs) @@ -1996,6 +2158,7 @@ class FichaPesquisaView(PermissionRequiredMixin, FormView): class FichaSelecionaView(PermissionRequiredMixin, FormView): + logger = logging.getLogger(__name__) form_class = FichaSelecionaForm template_name = 'materia/impressos/ficha_seleciona.html' permission_required = ('materia.can_access_impressos', ) @@ -2024,8 +2187,16 @@ class FichaSelecionaView(PermissionRequiredMixin, FormView): context['form'].fields['materia'].choices = [ (m.id, str(m)) for m in materia_list] + + username = self.request.user.username.replace("'","") if context['quantidade'] > 100: + self.logger.info('user=' + username + '. Sua pesquisa (tipo={}, data_inicial={}, data_final={}) retornou mais do que ' + '100 impressos. Por questões de ' + 'performance, foram retornados ' + 'apenas os 100 primeiros. Caso ' + 'queira outros, tente fazer uma ' + 'pesquisa mais específica'.format(tipo, data_inicial, data_final)) messages.info(self.request, _('Sua pesquisa retornou mais do que ' '100 impressos. Por questões de ' 'performance, foram retornados ' @@ -2037,11 +2208,14 @@ class FichaSelecionaView(PermissionRequiredMixin, FormView): def form_valid(self, form): context = {} + username = self.request.user.username.replace("'","") try: + self.logger.debug("user=" + username + ". Tentando obter objeto MateriaLegislativa com id={}".format(form.data['materia'])) materia = MateriaLegislativa.objects.get( id=form.data['materia']) except ObjectDoesNotExist: + self.logger.error("user=" + username + ". Esta MáteriaLegislativa não existe (id={}).".format(form.data['materia'])) mensagem = _('Esta Máteria não existe!') self.messages.add_message(self.request, messages.INFO, mensagem) diff --git a/sapl/norma/forms.py b/sapl/norma/forms.py index 818d6ba38..d4f0046a6 100644 --- a/sapl/norma/forms.py +++ b/sapl/norma/forms.py @@ -1,5 +1,6 @@ import django_filters +import logging from crispy_forms.helper import FormHelper from crispy_forms.layout import Fieldset, Layout from django import forms @@ -105,6 +106,8 @@ class NormaJuridicaForm(ModelForm): widget=forms.Select(attrs={'autocomplete': 'off'}) ) + logger = logging.getLogger(__name__) + class Meta: model = NormaJuridica fields = ['tipo', @@ -129,6 +132,7 @@ class NormaJuridicaForm(ModelForm): def clean(self): + cleaned_data = super(NormaJuridicaForm, self).clean() if not self.is_valid(): @@ -137,6 +141,7 @@ class NormaJuridicaForm(ModelForm): import re has_digits = re.sub('[^0-9]', '', cleaned_data['numero']) if not has_digits: + self.logger.error("Número de norma ({}) não pode conter somente letras.".format(cleaned_data['numero'])) raise ValidationError('Número de norma não pode conter somente letras') if self.instance.numero != cleaned_data['numero']: @@ -144,24 +149,35 @@ class NormaJuridicaForm(ModelForm): numero=cleaned_data['numero'], tipo=cleaned_data['tipo']).exists() if norma: + self.logger.error("Já existe uma norma de mesmo Tipo ({}), Ano ({}) " + "e Número ({}) no sistema." + .format(cleaned_data['tipo'], cleaned_data['ano'], cleaned_data['numero'])) raise ValidationError("Já existe uma norma de mesmo Tipo, Ano " "e Número no sistema") if (cleaned_data['tipo_materia'] and - cleaned_data['numero_materia'] and - cleaned_data['ano_materia']): + cleaned_data['numero_materia'] and + cleaned_data['ano_materia']): try: + self.logger.debug("Tentando obter objeto MateriaLegislativa com tipo={}, numero={}, ano={}." + .format(cleaned_data['tipo_materia'], cleaned_data['numero_materia'], cleaned_data['ano_materia'])) materia = MateriaLegislativa.objects.get( tipo_id=cleaned_data['tipo_materia'], numero=cleaned_data['numero_materia'], ano=cleaned_data['ano_materia']) except ObjectDoesNotExist: + self.logger.error("Matéria Legislativa %s/%s (%s) é inexistente." % ( + self.cleaned_data['numero_materia'], + self.cleaned_data['ano_materia'], + cleaned_data['tipo_materia'].descricao)) raise forms.ValidationError( _("Matéria Legislativa %s/%s (%s) é inexistente." % ( self.cleaned_data['numero_materia'], self.cleaned_data['ano_materia'], cleaned_data['tipo_materia'].descricao))) else: + self.logger.info("MateriaLegislativa com tipo={}, numero={}, ano={} obtida com sucesso." + .format(cleaned_data['tipo_materia'], cleaned_data['numero_materia'], cleaned_data['ano_materia'])) cleaned_data['materia'] = materia else: @@ -171,6 +187,8 @@ class NormaJuridicaForm(ModelForm): data = cleaned_data['data'] if data.year != ano: + self.logger.error("O ano da norma ({}) é diferente " + "do ano no campo data ({}).".format(ano, data.year)) raise ValidationError("O ano da norma não pode ser " "diferente do ano no campo data") return cleaned_data @@ -179,6 +197,8 @@ class NormaJuridicaForm(ModelForm): 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)) + tam_fornecido = str( texto_integral.size / (1024*1024) ) + self.logger.error("Arquivo muito grande ({}MB). ( Tamanho máximo permitido: {}MB )".format(tam_fornecido, max_size)) raise ValidationError( "Arquivo muito grande. ( > {0}MB )".format(max_size)) return texto_integral @@ -202,6 +222,8 @@ class AutoriaNormaForm(ModelForm): data_relativa = forms.DateField( widget=forms.HiddenInput(), required=False) + logger = logging.getLogger(__name__) + def __init__(self, *args, **kwargs): super(AutoriaNormaForm, self).__init__(*args, **kwargs) @@ -233,6 +255,7 @@ class AutoriaNormaForm(ModelForm): if ((not pk and autorias.exists()) or (pk and autorias.exclude(pk=pk).exists())): + self.logger.error("Autor ({}) já foi cadastrado.".format(cd['autor'])) raise ValidationError(_('Esse Autor já foi cadastrado.')) return cd @@ -245,6 +268,7 @@ class AnexoNormaJuridicaForm(ModelForm): 'norma': forms.HiddenInput(), } + logger = logging.getLogger(__name__) def clean(self): cleaned_data = super(AnexoNormaJuridicaForm, self).clean() if not self.is_valid(): @@ -252,6 +276,8 @@ class AnexoNormaJuridicaForm(ModelForm): anexo_arquivo = self.cleaned_data.get('anexo_arquivo', False) if anexo_arquivo and anexo_arquivo.size > MAX_DOC_UPLOAD_SIZE: max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) + tam_fornecido = str( anexo_arquivo.size / (1024*1024) ) + self.logger.error("Arquivo muito grande ({}MB). ( Tamanho máximo permitido: {}MB )".format(tam_fornecido, max_size)) raise ValidationError( "Arquivo muito grande. ( > {0}MB )".format(max_size)) return cleaned_data @@ -282,6 +308,7 @@ class NormaRelacionadaForm(ModelForm): required=False, widget=forms.Textarea(attrs={'disabled': 'disabled'})) + logger = logging.getLogger(__name__) class Meta: model = NormaRelacionada fields = ['tipo', 'numero', 'ano', 'ementa', 'tipo_vinculo'] @@ -297,14 +324,17 @@ class NormaRelacionadaForm(ModelForm): cleaned_data = self.cleaned_data try: + self.logger.debug("Tentando obter objeto NormaJuridica com numero={}, ano={}, tipo={}.".format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) norma_relacionada = NormaJuridica.objects.get( numero=cleaned_data['numero'], ano=cleaned_data['ano'], tipo=cleaned_data['tipo']) except ObjectDoesNotExist: + self.logger.info("NormaJuridica com numero={}, ano={}, tipo={} não existe.".format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) msg = _('A norma a ser relacionada não existe.') raise ValidationError(msg) else: + self.logger.info("NormaJuridica com numero={}, ano={}, tipo={} obtida com sucesso.".format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) cleaned_data['norma_relacionada'] = norma_relacionada return cleaned_data @@ -340,6 +370,8 @@ class NormaPesquisaSimplesForm(forms.Form): required=False, max_length=150) + logger = logging.getLogger(__name__) + def __init__(self, *args, **kwargs): super(NormaPesquisaSimplesForm, self).__init__(*args, **kwargs) @@ -373,12 +405,15 @@ class NormaPesquisaSimplesForm(forms.Form): if (data_inicial and data_final and data_inicial > data_final): + self.logger.error("Data Final ({}) menor que a Data Inicial ({}).".format(data_final, data_inicial)) raise ValidationError(_( 'A Data Final não pode ser menor que a Data Inicial')) else: condicao1 = data_inicial and not data_final condicao2 = not data_inicial and data_final if condicao1 or condicao2: + self.logger.error("Caso pesquise por data, os campos de Data Inicial e " + "Data Final devem ser preenchidos obrigatoriamente") raise ValidationError(_('Caso pesquise por data, os campos de Data Inicial e ' + 'Data Final devem ser preenchidos obrigatoriamente')) diff --git a/sapl/norma/views.py b/sapl/norma/views.py index b91aec2a5..faca58d32 100644 --- a/sapl/norma/views.py +++ b/sapl/norma/views.py @@ -1,5 +1,6 @@ import re +import logging from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ObjectDoesNotExist @@ -199,16 +200,22 @@ class NormaCrud(Crud): class CreateView(Crud.CreateView): form_class = NormaJuridicaForm + logger = logging.getLogger(__name__) + @property def cancel_url(self): return self.search_url def get_initial(self): + username = self.request.user.username + try: + self.logger.debug('user=' + username + '. Tentando obter objeto de modelo da esfera da federação.') esfera = sapl.base.models.AppConfig.objects.last( ).esfera_federacao self.initial['esfera_federacao'] = esfera except: + self.logger.error('user=' + username + '. Erro ao obter objeto de modelo da esfera da federação.') pass self.initial['complemento'] = False return self.initial @@ -241,17 +248,24 @@ class NormaCrud(Crud): def recuperar_norma(request): + logger = logging.getLogger(__name__) + username = request.user.username + tipo = TipoNormaJuridica.objects.get(pk=request.GET['tipo']) numero = request.GET['numero'] ano = request.GET['ano'] try: + logger.info('user=' + username + '. Tentando obter NormaJuridica (tipo={}, ano={}, numero={}).' + .format(tipo, ano, numero)) norma = NormaJuridica.objects.get(tipo=tipo, ano=ano, numero=numero) response = JsonResponse({'ementa': norma.ementa, 'id': norma.id}) except ObjectDoesNotExist: + logger.error('user=' + username + '. NormaJuridica buscada (tipo={}, ano={}, numero={}) não existe. ' + 'Definida com ementa vazia e id 0.'.format(tipo, ano, numero)) response = JsonResponse({'ementa': '', 'id': 0}) return response diff --git a/sapl/painel/views.py b/sapl/painel/views.py index 88d0aa785..963e53019 100644 --- a/sapl/painel/views.py +++ b/sapl/painel/views.py @@ -1,5 +1,6 @@ import html import json +import logging from django.contrib import messages from django.contrib.auth.decorators import user_passes_test @@ -42,6 +43,9 @@ def votacao_aberta(request): nenhuma. É utilizada como uma função auxiliar para a view votante_view. ''' + logger = logging.getLogger(__name__) + username = request.user.username + votacoes_abertas = SessaoPlenaria.objects.filter( Q(ordemdia__votacao_aberta=True) | Q(expedientemateria__votacao_aberta=True)).distinct() @@ -53,7 +57,9 @@ def votacao_aberta(request): reverse('sapl.sessao:sessaoplenaria_detail', kwargs={'pk': v.id}), v.__str__())) - + logger.info('user=' + username + '. Existe mais de uma votações aberta. Elas se encontram ' + 'nas seguintes Sessões: ' + ', '.join(msg_abertas) + '. ' + 'Para votar, peça para que o Operador feche-as.') msg = _('Existe mais de uma votações aberta. Elas se encontram ' 'nas seguintes Sessões: ' + ', '.join(msg_abertas) + '. ' 'Para votar, peça para que o Operador feche-as.') @@ -70,6 +76,11 @@ def votacao_aberta(request): numero_materias_abertas = len(ordens) + len(expedientes) if numero_materias_abertas > 1: + logger.info('user=' + username + '. Existe mais de uma votação aberta na Sessão: ' + + ('''
  • %s
  • ''' % ( + reverse('sapl.sessao:sessaoplenaria_detail', + kwargs={'pk': votacoes_abertas.first().id}), + votacoes_abertas.first().__str__()))) msg = _('Existe mais de uma votação aberta na Sessão: ' + ('''
  • %s
  • ''' % ( reverse('sapl.sessao:sessaoplenaria_detail', @@ -83,7 +94,7 @@ def votacao_aberta(request): def votacao(context,context_vars): - + logger = logging.getLogger(__name__) parlamentar = context_vars['votante'].parlamentar parlamentar_presente = False if parlamentar.id in context_vars['presentes']: @@ -105,13 +116,17 @@ def votacao(context,context_vars): if voto: try: + logger.debug("Tentando obter objeto VotoParlamentar com parlamentar={}.".format(context_vars['parlamentar'])) voto = voto.get(parlamentar=context_vars['parlamentar']) context.update({'voto_parlamentar': voto.voto}) except ObjectDoesNotExist: + logger.error("Voto do parlamentar {} não computado.".format(context_vars['parlamentar'])) context.update( {'voto_parlamentar': 'Voto não ' 'computado.'}) else: + logger.error("Parlamentar com id={} não está presente na " + "Ordem do Dia/Expediente em votação.".format(parlamentar.id)) context.update({'error_message': 'Você não está presente na ' 'Ordem do Dia/Expediente em votação.'}) @@ -185,14 +200,20 @@ def can_vote(context, context_vars, request): def votante_view(request): + logger = logging.getLogger(__name__) + username = request.user.username + # Pega o votante relacionado ao usuário template_name = 'painel/voto_nominal.html' context = {} context_vars = {} try: + logger.debug('user=' + username + '. Tentando obter objeto Votante com user={}.'.format(request.user)) votante = Votante.objects.get(user=request.user) except ObjectDoesNotExist: + logger.error("user=" + username + ". Usuário (user={}) não cadastrado como votante na tela de parlamentares. " + "Contate a administração de sua Casa Legislativa!".format(request.user)) msg = _("Usuário não cadastrado como votante na tela de parlamentares. Contate a administração de sua Casa Legislativa!") context.update({ 'error_message':msg @@ -205,8 +226,9 @@ def votante_view(request): # Verifica se usuário possui permissão para votar if 'parlamentares.can_vote' in request.user.get_all_permissions(): context, context_vars = can_vote(context, context_vars, request) - + logger.debug("user=" + username + ". Verificando se usuário {} possui permissão para votar.".format(request.user)) else: + logger.error("user=" + username + ". Usuário {} sem permissão para votar.".format(request.user)) context.update({'permissao': False, 'error_message': 'Usuário sem permissão para votar.'}) @@ -214,10 +236,14 @@ def votante_view(request): if request.method == 'POST': if context_vars['ordem_dia']: try: + logger.info("user=" + username + ". Tentando obter objeto VotoParlamentar para parlamentar={} e ordem={}." + .format(context_vars['parlamentar'], context_vars['ordem_dia'])) voto = VotoParlamentar.objects.get( parlamentar=context_vars['parlamentar'], ordem=context_vars['ordem_dia']) except ObjectDoesNotExist: + logger.error("user=" + username + ". Erro ao obter VotoParlamentar para parlamentar={} e ordem={}. Criando objeto." + .format(context_vars['parlamentar'], context_vars['ordem_dia'])) voto = VotoParlamentar.objects.create( parlamentar=context_vars['parlamentar'], voto=request.POST['voto'], @@ -225,6 +251,8 @@ def votante_view(request): ip=get_client_ip(request), ordem=context_vars['ordem_dia']) else: + logger.info("user=" + username + ". VotoParlamentar para parlamentar={} e ordem={} obtido com sucesso." + .format(context_vars['parlamentar'], context_vars['ordem_dia'])) voto.voto = request.POST['voto'] voto.ip = get_client_ip(request) voto.user = request.user @@ -232,10 +260,14 @@ def votante_view(request): elif context_vars['expediente']: try: + logger.info("user=" + username + ". Tentando obter objeto VotoParlamentar para parlamentar={} e expediente={}." + .format(context_vars['parlamentar'], context_vars['expediente'])) voto = VotoParlamentar.objects.get( parlamentar=context_vars['parlamentar'], expediente=context_vars['expediente']) except ObjectDoesNotExist: + logger.error("user=" + username + ". Erro ao obter VotoParlamentar para parlamentar={} e expediente={}. Criando objeto." + .format(context_vars['parlamentar'], context_vars['expediente'])) voto = VotoParlamentar.objects.create( parlamentar=context_vars['parlamentar'], voto=request.POST['voto'], @@ -243,6 +275,8 @@ def votante_view(request): ip=get_client_ip(request), expediente=context_vars['expediente']) else: + logger.info("user=" + username + ". VotoParlamentar para parlamentar={} e expediente={} obtido com sucesso." + .format(context_vars['parlamentar'], context_vars['expediente'])) voto.voto = request.POST['voto'] voto.ip = get_client_ip(request) voto.user = request.user @@ -304,9 +338,14 @@ def cronometro_painel(request): def get_cronometro_status(request, name): + logger = logging.getLogger(__name__) + username = request.user.username + try: + logger.debug("user=" + username + ". Tentando obter cronometro.") cronometro = request.session[name] - except KeyError: + except KeyError as e: + logger.error("user=" + username + ". Erro ao obter cronometro. Retornado como vazio. " + str(e)) cronometro = '' return cronometro @@ -402,6 +441,7 @@ def response_nenhuma_materia(response): def get_votos(response, materia): + logger = logging.getLogger(__name__) if type(materia) == OrdemDia: registro = RegistroVotacao.objects.filter( ordem=materia, materia=materia.materia).last() @@ -433,9 +473,12 @@ def get_votos(response, materia): for i, p in enumerate(response['presentes']): try: + logger.info("Tentando obter votos do parlamentar (id={}).".format(p['parlamentar_id'])) if votos_parlamentares.get(parlamentar_id=p['parlamentar_id']).voto: response['presentes'][i]['voto'] = 'Voto Informado' except ObjectDoesNotExist: + logger.error("Votos do parlamentar (id={}) não encontrados. Retornado vazio." + .format(p['parlamentar_id'])) response['presentes'][i]['voto'] = '' else: @@ -450,9 +493,11 @@ def get_votos(response, materia): for i, p in enumerate(response['presentes']): try: + logger.debug("Tentando obter votos do parlamentar (id={}).".format(p['parlamentar_id'])) response['presentes'][i]['voto'] = votos_parlamentares.get( parlamentar_id=p['parlamentar_id']).voto except ObjectDoesNotExist: + logger.error("Votos do parlamentar (id={}) não encontrados. Retornado None.".format(p['parlamentar_id'])) response['presentes'][i]['voto'] = None response.update({ diff --git a/sapl/parlamentares/forms.py b/sapl/parlamentares/forms.py old mode 100644 new mode 100755 index 6ae314785..36237d448 --- a/sapl/parlamentares/forms.py +++ b/sapl/parlamentares/forms.py @@ -1,3 +1,5 @@ +import logging + from datetime import timedelta from crispy_forms.helper import FormHelper @@ -40,9 +42,12 @@ class CustomImageCropWidget(ImageCropWidget): def validar_datas_legislatura(eleicao, inicio, fim, pk=None): - + logger = logging.getLogger(__name__) # Verifica se data de eleição < inicio < fim if inicio >= fim or eleicao >= inicio: + logger.error('A data início ({}) deve ser menor que a ' + + 'data fim ({}) e a data eleição ({}) deve ser ' + + 'menor que a data início ({})'.format(inicio, fim, eleicao, inicio)) msg_error = _('A data início deve ser menor que a ' + 'data fim e a data eleição deve ser ' + 'menor que a data início') @@ -53,12 +58,15 @@ def validar_datas_legislatura(eleicao, inicio, fim, pk=None): data_inicio__lte=fim, data_fim__gte=inicio ).exclude(pk=pk).exists() if intersecao_legislatura: + logger.error("Já existe uma legislatura neste intervalo de datas (data_inicio<={} e data_fim>={})." + .format(fim, inicio)) msg_error = _('Já existe uma legislatura neste intervalo de datas') return (False, msg_error) # Verifica se há alguma outra data de eleição cadastrada if Legislatura.objects.filter( data_eleicao=eleicao).exclude(pk=pk).exists(): + logger.error("Esta data de eleição ({}) já foi cadastrada.".format(eleicao)) msg_error = _('Esta data de eleição já foi cadastrada') return (False, msg_error) @@ -66,7 +74,7 @@ def validar_datas_legislatura(eleicao, inicio, fim, pk=None): class MandatoForm(ModelForm): - + logger = logging.getLogger(__name__) class Meta: model = Mandato fields = ['legislatura', 'coligacao', 'votos_recebidos', @@ -89,6 +97,9 @@ class MandatoForm(ModelForm): if data_inicio_mandato: if (data_inicio_mandato < legislatura.data_inicio or data_inicio_mandato > legislatura.data_fim): + self.logger.error("Data início mandato ({}) fora do intervalo" + " de legislatura informada ({} a {})." + .format(data_inicio_mandato, legislatura.data_inicio, legislatura.data_fim)) raise ValidationError(_("Data início mandato fora do intervalo" " de legislatura informada")) @@ -96,17 +107,27 @@ class MandatoForm(ModelForm): if data_fim_mandato: if (data_fim_mandato < legislatura.data_inicio or data_fim_mandato > legislatura.data_fim): + self.logger.error("Data fim mandato ({}) fora do intervalo" + " de legislatura informada ({} a {})." + .format(data_fim_mandato, legislatura.data_inicio, legislatura.data_fim)) raise ValidationError(_("Data fim mandato fora do intervalo de" " legislatura informada")) data_expedicao_diploma = data['data_expedicao_diploma'] if (data_expedicao_diploma and data_expedicao_diploma > data_inicio_mandato): + self.logger.error("A data da expedição do diploma ({}) deve ser anterior " + "a data de início do mandato ({}).".format(data_expedicao_diploma, data_inicio_mandato)) raise ValidationError(_("A data da expedição do diploma deve ser anterior " "a data de início do mandato")) coligacao = data['coligacao'] if coligacao and not coligacao.legislatura == legislatura: + self.logger.error("A coligação selecionada ({}) não está cadastrada " + "na mesma legislatura ({}) que o presente mandato ({}), " + "favor verificar a coligação ou fazer o cadastro " + "de uma nova coligação na legislatura correspondente" + .format(coligacao, coligacao.legislatura, legislatura)) raise ValidationError(_("A coligação selecionada não está cadastrada " "na mesma legislatura que o presente mandato, " "favor verificar a coligação ou fazer o cadastro " @@ -116,6 +137,8 @@ class MandatoForm(ModelForm): parlamentar=data['parlamentar'], legislatura=data['legislatura']).exists() if existe_mandato: + self.logger.error("Mandato nesta legislatura (parlamentar={}, legislatura={}) já existe." + .format(data['parlamentar'], data['legislatura'])) raise ValidationError(_('Mandato nesta legislatura já existe.')) return self.cleaned_data @@ -123,6 +146,8 @@ class MandatoForm(ModelForm): class LegislaturaForm(ModelForm): + logger = logging.getLogger(__name__) + class Meta: model = Legislatura exclude = [] @@ -148,8 +173,13 @@ class LegislaturaForm(ModelForm): ).order_by('data_fim').first() if ultima_legislatura and ultima_legislatura.numero >= numero: + self.logger.error("Número ({}) deve ser maior que o da legislatura anterior ({})." + .format(numero, ultima_legislatura.numero)) raise ValidationError(_("Número deve ser maior que o da legislatura anterior")) elif proxima_legislatura and proxima_legislatura.numero <= numero: + self.logger.error("O Número ({}) deve ser menor que {}, pois existe uma " + "legislatura afrente cronologicamente desta que está sendo criada!" + .format(numero, proxima_legislatura.numero)) msg_erro = "O Número deve ser menor que {}, pois existe uma " \ "legislatura afrente cronologicamente desta que está sendo criada!" msg_erro = msg_erro.format(proxima_legislatura.numero) @@ -222,8 +252,13 @@ class ParlamentarCreateForm(ParlamentarForm): def validar_datas(data_filiacao, data_desfiliacao, parlamentar, filiacao): + + logger = logging.getLogger(__name__) + # Verifica se data de desfiliacao é anterior a data de filiacao if data_desfiliacao and data_desfiliacao < data_filiacao: + logger.error("A data de desfiliação ({}) é anterior à data de filiação ({})." + .format(data_desfiliacao, data_filiacao)) error_msg = _("A data de desfiliação não pode anterior \ à data de filiação") return [False, error_msg] @@ -249,6 +284,8 @@ def validar_datas(data_filiacao, data_desfiliacao, parlamentar, filiacao): # filiação em edição não é a última e está sem data de desfiliação if not data_desfiliacao and filiacao_em_edicao_id and\ filiacao_em_edicao_id != filiacoes.last().pk: + logger.error("Data de desfiliação do parlamentar não pode ser " + "ausente, se existirem datas de filiação posteriores.") error_msg = _("Data de desfiliação do parlamentar não pode ser\ ausente, se existirem datas de filiação posteriores.") @@ -256,6 +293,8 @@ def validar_datas(data_filiacao, data_desfiliacao, parlamentar, filiacao): # já existe outra sem data de desfiliação elif not data_desfiliacao and not filiacao_em_edicao_id and\ not filiacoes.last().data_desfiliacao: + logger.error("O parlamentar não pode se filiar a novo partido sem" + " antes se desfiliar do partido anterior.") error_msg = _("O parlamentar não pode se filiar a novo partido sem\ antes se desfiliar do partido anterior.") @@ -266,14 +305,20 @@ def validar_datas(data_filiacao, data_desfiliacao, parlamentar, filiacao): # testa a intercessão de intervalo com outra filiação if filiacoes.filter(range_livre_exigido).exists(): - error_msg = _("A data de filiação e desfiliação não podem estar\ - no intervalo de outro período de filiação.") + logger.error("A data de filiação e desfiliação não podem estar" + " no intervalo de outro período de filiação.") + error_msg = _("A data de filiação e desfiliação (intervalo de {} a {}) " + "não podem estar no intervalo de outro período de filiação." + .format(data_filiacao, df_desfiliacao, )) if not error_msg: # passou pelo teste de intervalo mas a data de filiação é maior que # a ultima que está em aberto if filiacoes.filter(data_desfiliacao__isnull=True, data__lte=data_filiacao).exists(): + logger.error("Não pode haver um registro de filiação com data de " + "filiação igual ou superior a data de filiação em aberto ({})." + .format(data_filiacao)) error_msg = _("Não pode haver um registro de filiação com data de \ filiação igual ou superior a data de filiação em aberto.") @@ -313,6 +358,8 @@ class FiliacaoForm(ModelForm): class ComposicaoColigacaoForm(ModelForm): + logger = logging.getLogger(__name__) + class Meta: model = ComposicaoColigacao fields = ['partido'] @@ -328,6 +375,8 @@ class ComposicaoColigacaoForm(ModelForm): if (ComposicaoColigacao.objects.filter( coligacao_id=pk, partido=cleaned_data.get('partido')).exists()): + self.logger.error("Esse partido (coligacao_id={} e partido={}) já foi cadastrado " + "nesta coligação.".format(pk, cleaned_data.get('partido'))) msg = _('Esse partido já foi cadastrado nesta coligação.') raise ValidationError(msg) @@ -335,7 +384,7 @@ class ComposicaoColigacaoForm(ModelForm): class FrenteForm(ModelForm): - + logger = logging.getLogger(__name__) def __init__(self, *args, **kwargs): super(FrenteForm, self).__init__(*args, **kwargs) self.fields['parlamentares'].queryset = Parlamentar.objects.filter( @@ -354,6 +403,8 @@ class FrenteForm(ModelForm): return self.cleaned_data if cd['data_extincao'] and cd['data_criacao'] >= cd['data_extincao']: + self.logger.error("Data Dissolução ({}) não pode ser anterior a Data Criação ({})." + .format(cd['data_extincao'],cd['data_criacao'])) raise ValidationError(_("Data Dissolução não pode ser anterior a Data Criação")) return cd @@ -383,6 +434,8 @@ class VotanteForm(ModelForm): required=True, max_length=30) + logger = logging.getLogger(__name__) + class Meta: model = Votante fields = ['username'] @@ -413,12 +466,15 @@ class VotanteForm(ModelForm): username = cd['username'] user = get_user_model().objects.filter(username=username) if not user.exists(): + self.logger.error("Não foi possível vincular usuário. Usuário {} não existe.".format(username)) raise ValidationError(_( "{} [{}] {}".format( 'Não foi possível vincular usuário. Usuário', username, 'não existe'))) if Votante.objects.filter(user=user[0].pk).exists(): + self.logger.error("Não foi possível vincular usuário. Usuário {} já está " + "vinculado à outro parlamentar.".format(username)) raise ValidationError(_( "{} [{}] {}".format( 'Não foi possível vincular usuário. Usuário', diff --git a/sapl/parlamentares/views.py b/sapl/parlamentares/views.py index f3fb81de5..9e6fc1d17 100644 --- a/sapl/parlamentares/views.py +++ b/sapl/parlamentares/views.py @@ -1,4 +1,5 @@ import json +import logging from datetime import datetime from django.contrib import messages @@ -272,11 +273,16 @@ def parlamentares_frente_selected(request): """ :return: Lista com o id dos parlamentares em uma frente """ + logger = logging.getLogger(__name__) + username = request.user.username try: + logger.info("user=" + username + ". Tentando objet objeto Frente com id={}.".format(request.GET['frente_id'])) frente = Frente.objects.get(id=int(request.GET['frente_id'])) except ObjectDoesNotExist: + logger.error("user=" + username + ". Frente buscada (id={}) não existe. Retornada lista vazia.".format(request.GET['frente_id'])) lista_parlamentar_id = [] else: + logger.info("user=" + username + ". Frente (id={}) encontrada com sucesso.".format(request.GET['frente_id'])) lista_parlamentar_id = frente.parlamentares.all().values_list( 'id', flat=True) return JsonResponse({'id_list': list(lista_parlamentar_id)}) @@ -354,17 +360,22 @@ class ComposicaoColigacaoCrud(MasterDetailCrud): class LegislaturaCrud(CrudAux): + model = Legislatura help_topic = 'legislatura' class CreateView(CrudAux.CreateView): + logger = logging.getLogger(__name__) form_class = LegislaturaForm def get_initial(self): + username = self.request.user.username try: + self.logger.error("user=" + username + ". Tentando obter última Legislatura.") ultima_legislatura = Legislatura.objects.latest('numero') numero = ultima_legislatura.numero + 1 except Legislatura.DoesNotExist: + self.logger.error("user=" + username + ". Legislatura não encontrada. Número definido como 1.") numero = 1 return {'numero': numero} @@ -456,15 +467,19 @@ class ParlamentarCrud(Crud): class ListView(Crud.ListView): template_name = "parlamentares/parlamentares_list.html" paginate_by = None + logger = logging.getLogger(__name__) @xframe_options_exempt def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) def take_legislatura_id(self): + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter id da legislatura.") return int(self.request.GET['pk']) except: + self.logger.error("user=" + username + ". Legislatura não possui ID. Buscando em todas as entradas.") legislaturas = Legislatura.objects.all() for l in legislaturas: if l.atual(): @@ -474,21 +489,26 @@ class ParlamentarCrud(Crud): return -1 def get_queryset(self): + self.logger = logging.getLogger(__name__) queryset = super().get_queryset() legislatura_id = self.take_legislatura_id() # Pelo menos uma casa legislativa criou uma # legislatura de numero zero, o que é um absurdo + username = self.request.user.username if legislatura_id >= 0: return queryset.filter( mandato__legislatura_id=legislatura_id).annotate( mandato_titular=F('mandato__titular')) else: try: + self.logger.debug("user=" + username + ". Tentando obter o mais recente registro do objeto Legislatura.") l = Legislatura.objects.all().order_by( '-data_inicio').first() except ObjectDoesNotExist: + self.logger.error("user=" + username + ". Objeto não encontrado. Retornando todos os registros.") return Legislatura.objects.all() else: + self.logger.info("user=" + username + ". Objeto encontrado com sucesso.") if l is None: return Legislatura.objects.all() return queryset.filter(mandato__legislatura_id=l).annotate( @@ -500,6 +520,7 @@ class ParlamentarCrud(Crud): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + username = self.request.user.username # Adiciona legislatura para filtrar parlamentares legislaturas = Legislatura.objects.all().order_by('-numero') @@ -525,6 +546,9 @@ class ParlamentarCrud(Crud): # da legislatura e data de desfiliação deve nula, ou maior, # ou igual a data de fim da legislatura try: + self.logger.debug("user=" + username + ". Tentando obter filiação do parlamentar com (data<={} e data_desfiliacao>={}) " + "ou (data<={} e data_desfiliacao=Null))." + .format(legislatura.data_fim, legislatura.data_fim, legislatura.data_fim)) filiacao = parlamentar.filiacao_set.get(Q( data__lte=legislatura.data_fim, data_desfiliacao__gte=legislatura.data_fim) | Q( @@ -533,17 +557,24 @@ class ParlamentarCrud(Crud): # Caso não exista filiação com essas condições except ObjectDoesNotExist: + self.logger.error("user=" + username + ". Parlamentar com (data<={} e data_desfiliacao>={}) " + "ou (data<={} e data_desfiliacao=Null)) não possui filiação." + .format(legislatura.data_fim, legislatura.data_fim, legislatura.data_fim)) row[1] = ('Não possui filiação', None, None) # Caso exista mais de uma filiação nesse intervalo # Entretanto, NÃO DEVE OCORRER except MultipleObjectsReturned: + self.logger.error("user=" + username + ". O Parlamentar com (data<={} e data_desfiliacao>={}) " + "ou (data<={} e data_desfiliacao=Null)) possui duas filiações conflitantes" + .format(legislatura.data_fim, legislatura.data_fim, legislatura.data_fim)) row[1] = ( 'O Parlamentar possui duas filiações conflitantes', None) # Caso encontre UMA filiação nessas condições else: + self.logger.info("user=" + username + ". Filiação encontrada com sucesso.") row[1] = (filiacao.partido.sigla, None, None) return context @@ -552,6 +583,7 @@ class ParlamentarCrud(Crud): class ParlamentarMateriasView(FormView): template_name = "parlamentares/materias.html" success_url = reverse_lazy('sapl.parlamentares:parlamentar_materia') + logger = logging.getLogger(__name__) def get_autoria(self, resultset): autoria = {} @@ -572,13 +604,15 @@ class ParlamentarMateriasView(FormView): @xframe_options_exempt def get(self, request, *args, **kwargs): parlamentar_pk = kwargs['pk'] - + username = request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter Autor (object_id={}).".format(parlamentar_pk)) autor = Autor.objects.get( content_type=ContentType.objects.get_for_model(Parlamentar), object_id=parlamentar_pk) except ObjectDoesNotExist: - mensagem = _('Este Parlamentar não é autor de matéria.') + mensagem = _('Este Parlamentar (pk={}) não é Autor de matéria.'.format(parlamentar_pk)) + self.logger.error("user=" + username + ". Este Parlamentar (pk={}) não é Autor de matéria.".format(parlamentar_pk)) messages.add_message(request, messages.ERROR, mensagem) return HttpResponseRedirect( reverse( @@ -620,6 +654,7 @@ class ParlamentarMateriasView(FormView): class MesaDiretoraView(FormView): template_name = 'parlamentares/composicaomesa_form.html' success_url = reverse_lazy('sapl.parlamentares:mesa_diretora') + logger = logging.getLogger(__name__) def get_template_names(self): if self.request.user.has_perm('parlamentares.change_composicaomesa'): @@ -633,6 +668,10 @@ class MesaDiretoraView(FormView): # Essa função avisa quando se pode compor uma Mesa Legislativa def validation(self, request): + username = request.user.username + self.logger.info('user=' + username + '. Não há nenhuma Sessão Legislativa cadastrada. ' + + 'Só é possível compor uma Mesa Diretora quando ' + + 'há uma Sessão Legislativa cadastrada.') mensagem = _('Não há nenhuma Sessão Legislativa cadastrada. ' + 'Só é possível compor uma Mesa Diretora quando ' + 'há uma Sessão Legislativa cadastrada.') @@ -698,9 +737,11 @@ def altera_field_mesa(request): operação (Legislatura/Sessão/Inclusão/Remoção), atualizando os campos após cada alteração """ + logger = logging.getLogger(__name__) legislatura = request.GET['legislatura'] sessoes = SessaoLegislativa.objects.filter( legislatura=legislatura).order_by('-data_inicio') + username = request.user.username if not sessoes: return JsonResponse({'msg': ('Nenhuma sessão encontrada!', 0)}) @@ -715,8 +756,11 @@ def altera_field_mesa(request): else: year = timezone.now().year try: + logger.debug("user=" + username + ". Tentando obter id de sessoes com data_inicio.ano={}.".format(year)) sessao_selecionada = sessoes.get(data_inicio__year=year).id except ObjectDoesNotExist: + logger.error("user=" + username + ". Id de sessoes com data_inicio.ano={} não encontrado. " + "Selecionado o ID da primeira sessão.".format(year)) sessao_selecionada = sessoes.first().id # Atualiza os componentes da view após a mudança @@ -756,7 +800,8 @@ def insere_parlamentar_composicao(request): Essa função lida com qualquer operação de inserção na composição da Mesa Diretora """ - + logger = logging.getLogger(__name__) + username = request.user.username if request.user.has_perm( '%s.add_%s' % ( AppConfig.label, ComposicaoMesa._meta.model_name)): @@ -764,19 +809,24 @@ def insere_parlamentar_composicao(request): composicao = ComposicaoMesa() try: + logger.debug("user=" + username + ". Tentando obter SessaoLegislativa com id={}.".format(request.POST['sessao'])) composicao.sessao_legislativa = SessaoLegislativa.objects.get( id=int(request.POST['sessao'])) except MultiValueDictKeyError: + logger.error("user=" + username + ". 'MultiValueDictKeyError', nenhuma sessão foi inserida!") return JsonResponse({'msg': ('Nenhuma sessão foi inserida!', 0)}) try: + logger.debug("user=" + username + ". Tentando obter Parlamentar com id={}.".format(request.POST['parlamentar'])) composicao.parlamentar = Parlamentar.objects.get( id=int(request.POST['parlamentar'])) except MultiValueDictKeyError: + logger.error("user=" + username + ". 'MultiValueDictKeyError', nenhum parlamentar foi inserido!") return JsonResponse({ 'msg': ('Nenhum parlamentar foi inserido!', 0)}) try: + logger.info("user=" + username + ". Tentando obter CargoMesa com id={}.".format(request.POST['cargo'])) composicao.cargo = CargoMesa.objects.get( id=int(request.POST['cargo'])) parlamentar_ja_inserido = ComposicaoMesa.objects.filter( @@ -789,11 +839,14 @@ def insere_parlamentar_composicao(request): composicao.save() except MultiValueDictKeyError: + logger.error("user=" + username + ". 'MultiValueDictKeyError', nenhum cargo foi inserido!") return JsonResponse({'msg': ('Nenhum cargo foi inserido!', 0)}) + logger.info("user=" + username + ". Parlamentar inserido com sucesso!") return JsonResponse({'msg': ('Parlamentar inserido com sucesso!', 1)}) else: + logger.error("user=" + username + " não tem permissão para esta operação!") return JsonResponse( {'msg': ('Você não tem permissão para esta operação!', 0)}) @@ -803,26 +856,32 @@ def remove_parlamentar_composicao(request): Essa função lida com qualquer operação de remoção na composição da Mesa Diretora """ - + logger = logging.getLogger(__name__) + username = request.user.username if request.POST and request.user.has_perm( '%s.delete_%s' % ( AppConfig.label, ComposicaoMesa._meta.model_name)): if 'composicao_mesa' in request.POST: try: + logger.debug("user=" + username + ". Tentando obter ComposicaoMesa com id={}.".format(request.POST['composicao_mesa'])) composicao = ComposicaoMesa.objects.get( id=request.POST['composicao_mesa']) except ObjectDoesNotExist: + logger.error("user=" + username + ". ComposicaoMesa com id={} não encontrada, portanto não pode ser removida." + .format(request.POST['composicao_mesa'])) return JsonResponse( {'msg': ( 'Composição da Mesa não pôde ser removida!', 0)}) composicao.delete() + logger.info("user=" + username + ". ComposicaoMesa com id={} excluido com sucesso!".format(request.POST['composicao_mesa'])) return JsonResponse( {'msg': ( 'Parlamentar excluido com sucesso!', 1)}) else: + logger.info("user=" + username + ". Nenhum parlamentar escolhido para ser excluído.") return JsonResponse( {'msg': ( 'Selecione algum parlamentar para ser excluido!', 0)}) @@ -838,7 +897,13 @@ def partido_parlamentar_sessao_legislativa(sessao, parlamentar): # A data de filiacao deve ser menor que a data de fim # da sessao legislativa e data de desfiliação deve nula, ou maior, # ou igual a data de fim da sessao + logger = logging.getLogger(__name__) try: + logger.debug("Tentando obter filiação do parlamentar com (data<={} e data_desfiliacao>={}) " + "ou (data<={} e data_desfiliacao=Null))." + .format(sessao.data_fim, sessao.data_fim, sessao.data_fim)) + + logger.info("Tentando obter filiação correspondente.") filiacao = parlamentar.filiacao_set.get(Q( data__lte=sessao.data_fim, data_desfiliacao__gte=sessao.data_fim) | Q( @@ -847,15 +912,24 @@ def partido_parlamentar_sessao_legislativa(sessao, parlamentar): # Caso não exista filiação com essas condições except ObjectDoesNotExist: + logger.error("Filiação do parlamentar com (data<={} e data_desfiliacao>={}) " + "ou (data<={} e data_desfiliacao=Null não encontrada. Retornando vazio." + .format(sessao.data_fim, sessao.data_fim, sessao.data_fim)) return '' # Caso exista mais de uma filiação nesse intervalo # Entretanto, NÃO DEVE OCORRER except MultipleObjectsReturned: + logger.error("O Parlamentar com (data<={} e data_desfiliacao>={}) " + "ou (data<={} e data_desfiliacao=Null possui duas filiações conflitantes." + .format(sessao.data_fim, sessao.data_fim, sessao.data_fim)) return 'O Parlamentar possui duas filiações conflitantes' # Caso encontre UMA filiação nessas condições else: + logger.info("Filiação do parlamentar com (data<={} e data_desfiliacao>={}) " + "ou (data<={} e data_desfiliacao=Null encontrada com sucesso." + .format(sessao.data_fim, sessao.data_fim, sessao.data_fim)) return filiacao.partido.sigla @@ -865,7 +939,8 @@ def altera_field_mesa_public_view(request): da Mesa Diretora para usuários anônimos, atualizando os campos após cada alteração """ - + logger = logging.getLogger(__name__) + username = request.user.username legislatura = request.GET['legislatura'] sessoes = SessaoLegislativa.objects.filter( legislatura=legislatura).order_by('-data_inicio') @@ -882,8 +957,11 @@ def altera_field_mesa_public_view(request): else: try: year = timezone.now().year + logger.info("user=" + username + ". Tentando obter sessões com data_inicio.ano = {}.".format(year)) sessao_selecionada = sessoes.get(data_inicio__year=year).id - except ObjectDoesNotExist as e: + except ObjectDoesNotExist: + logger.error("user=" + username + ". Sessões não encontradas com com data_inicio.ano = {}. " + "Selecionado o id da primeira sessão.".format(year)) sessao_selecionada = sessoes.first().id # Atualiza os componentes da view após a mudança diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py index 8c441ad93..a621754b7 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -1,5 +1,6 @@ import django_filters +import logging from crispy_forms.bootstrap import InlineRadios from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout @@ -65,7 +66,7 @@ class AcompanhamentoDocumentoForm(ModelForm): class ProtocoloFilterSet(django_filters.FilterSet): - filter_overrides = {models.DateField: { + filter_overrides = {models.DateTimeField: { 'filter_class': django_filters.DateFromToRangeFilter, 'extra': lambda f: { 'label': 'Data (%s)' % (_('Inicial - Final')), @@ -101,7 +102,7 @@ class ProtocoloFilterSet(django_filters.FilterSet): model = Protocolo fields = ['numero', 'tipo_documento', - 'data', + 'timestamp', 'tipo_materia', ] @@ -114,7 +115,7 @@ class ProtocoloFilterSet(django_filters.FilterSet): row1 = to_row( [('numero', 4), ('ano', 4), - ('data', 4)]) + ('timestamp', 4)]) row2 = to_row( [('tipo_documento', 4), @@ -226,6 +227,8 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet): class AnularProcoloAdmForm(ModelForm): + logger = logging.getLogger(__name__) + numero = forms.CharField(required=True, label=Protocolo._meta. get_field('numero').verbose_name @@ -252,12 +255,15 @@ class AnularProcoloAdmForm(ModelForm): ano = cleaned_data['ano'] try: + self.logger.debug("Tentando obter Protocolo com numero={} e ano={}.".format(numero, ano)) protocolo = Protocolo.objects.get(numero=numero, ano=ano) if protocolo.anulado: + self.logger.error("Protocolo %s/%s já encontra-se anulado" % (numero, ano)) raise forms.ValidationError( _("Protocolo %s/%s já encontra-se anulado") % (numero, ano)) except ObjectDoesNotExist: + self.logger.error("Protocolo %s/%s não existe" % (numero, ano)) raise forms.ValidationError( _("Protocolo %s/%s não existe" % (numero, ano))) @@ -270,6 +276,8 @@ class AnularProcoloAdmForm(ModelForm): ).order_by('-ano', '-numero').exists() if exists: + self.logger.error("Protocolo %s/%s não pode ser removido pois existem " + "documentos vinculados a ele." % (numero, ano)) raise forms.ValidationError( _("Protocolo %s/%s não pode ser removido pois existem " "documentos vinculados a ele." % (numero, ano))) @@ -386,6 +394,9 @@ class ProtocoloDocumentForm(ModelForm): class ProtocoloMateriaForm(ModelForm): + + logger = logging.getLogger(__name__) + autor = forms.ModelChoiceField(required=True, empty_label='------', queryset=Autor.objects.all() @@ -441,10 +452,13 @@ class ProtocoloMateriaForm(ModelForm): def clean_autor(self): autor_field = self.cleaned_data['autor'] try: + self.logger.debug("Tentando obter Autor com id={}.".format(autor_field.id)) autor = Autor.objects.get(id=autor_field.id) except ObjectDoesNotExist: + self.logger.error("Autor com id={} não encontrado. Definido como None.".format(autor_field.id)) autor_field = None else: + self.logger.info("Autor com id={} encontrado com sucesso.".format(autor_field.id)) autor_field = autor return autor_field @@ -459,15 +473,22 @@ class ProtocoloMateriaForm(ModelForm): if data['vincular_materia'] == 'True': try: if not data['ano_materia'] or not data['numero_materia']: + self.logger.error("Não foram informados o número ou ano da matéria a ser vinculada") raise ValidationError( 'Favor informar o número e ano da matéria a ser vinculada') + self.logger.debug("Tentando obter MateriaLegislativa com ano={}, numero={} e data={}." + .format(data['ano_materia'], data['numero_materia'], data['tipo_materia'])) self.materia = MateriaLegislativa.objects.get(ano=data['ano_materia'], numero=data['numero_materia'], tipo=data['tipo_materia']) if self.materia.numero_protocolo: + self.logger.error("MateriaLegislativa informada já possui o protocolo {}/{} vinculado." + .format(self.materia.numero_protocolo, self.materia.ano)) raise ValidationError(_('Matéria Legislativa informada já possui o protocolo {}/{} vinculado.' .format(self.materia.numero_protocolo, self.materia.ano))) except ObjectDoesNotExist: + self.logger.error("MateriaLegislativa informada (ano={}, numero={} e data={}) não existente." + .format(data['ano_materia'], data['numero_materia'], data['tipo_materia'])) raise ValidationError(_('Matéria Legislativa informada não existente.')) return data @@ -528,6 +549,8 @@ class DocumentoAcessorioAdministrativoForm(ModelForm): class TramitacaoAdmForm(ModelForm): + logger = logging.getLogger(__name__) + class Meta: model = TramitacaoAdministrativo fields = ['data_tramitacao', @@ -565,11 +588,17 @@ class TramitacaoAdmForm(ModelForm): if ultima_tramitacao: destino = ultima_tramitacao.unidade_tramitacao_destino if (destino != self.cleaned_data['unidade_tramitacao_local']): + self.logger.error('A origem da nova tramitação ({}) deve ser ' + 'igual ao destino ({}) da última adicionada!' + .format(self.cleaned_data['unidade_tramitacao_local'], destino)) msg = _('A origem da nova tramitação deve ser igual ao ' 'destino da última adicionada!') raise ValidationError(msg) if self.cleaned_data['data_tramitacao'] > timezone.now().date(): + self.logger.error('A data de tramitação ({}) deve ser ' + 'menor ou igual a data de hoje ({})!' + .format(self.cleaned_data['data_tramitacao'], timezone.now().date())) msg = _( 'A data de tramitação deve ser ' + 'menor ou igual a data de hoje!') @@ -577,18 +606,27 @@ class TramitacaoAdmForm(ModelForm): if (ultima_tramitacao and data_tram_form < ultima_tramitacao.data_tramitacao): + self.logger.error('A data da nova tramitação ({}) deve ser ' + 'maior que a data da última tramitação ({})!' + .format(data_tram_form, ultima_tramitacao.data_tramitacao)) msg = _('A data da nova tramitação deve ser ' + 'maior que a data da última tramitação!') raise ValidationError(msg) if data_enc_form: if data_enc_form < data_tram_form: + self.logger.error('A data de encaminhamento ({}) deve ser ' + 'maior que a data de tramitação ({})!' + .format(data_enc_form, data_tram_form)) msg = _('A data de encaminhamento deve ser ' + 'maior que a data de tramitação!') raise ValidationError(msg) if data_prazo_form: if data_prazo_form < data_tram_form: + self.logger.error('A data fim de prazo ({}) deve ser ' + 'maior que a data de tramitação ({})!' + .format(data_prazo_form, data_tram_form)) msg = _('A data fim de prazo deve ser ' + 'maior que a data de tramitação!') raise ValidationError(msg) @@ -604,6 +642,8 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm): data_tramitacao = forms.DateField(widget=forms.HiddenInput()) + logger = logging.getLogger(__name__) + class Meta: model = TramitacaoAdministrativo fields = ['data_tramitacao', @@ -631,6 +671,9 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm): if ultima_tramitacao != self.instance: if self.cleaned_data['unidade_tramitacao_destino'] != \ self.instance.unidade_tramitacao_destino: + self.logger.error('Você não pode mudar a Unidade de Destino desta ' + 'tramitação (id={}), pois irá conflitar com a Unidade ' + 'Local da tramitação seguinte'.format(self.instance.documento_id)) raise ValidationError( 'Você não pode mudar a Unidade de Destino desta ' 'tramitação, pois irá conflitar com a Unidade ' @@ -646,6 +689,8 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm): class DocumentoAdministrativoForm(ModelForm): + logger = logging.getLogger(__name__) + data = forms.DateField(initial=timezone.now) ano_protocolo = forms.ChoiceField(required=False, @@ -711,19 +756,26 @@ class DocumentoAdministrativoForm(ModelForm): tipo=tipo_documento, ano=ano_protocolo).exists() if doc_exists: + self.logger.error("DocumentoAdministrativo (numero={}, tipo={} e ano={}) já existe." + .format(numero_documento, tipo_documento, ano_protocolo)) raise ValidationError(_('Documento já existente')) # campos opcionais, mas que se informados devem ser válidos if numero_protocolo and ano_protocolo: try: + self.logger.debug("Tentando obter Protocolo com numero={} e ano={}." + .format(numero_protocolo, ano_protocolo)) self.fields['protocolo'].initial = Protocolo.objects.get( numero=numero_protocolo, ano=ano_protocolo).pk except ObjectDoesNotExist: + self.logger.error("Protocolo %s/%s inexistente." % ( + numero_protocolo, ano_protocolo)) msg = _('Protocolo %s/%s inexistente.' % ( numero_protocolo, ano_protocolo)) raise ValidationError(msg) except MultipleObjectsReturned: + self.logger.error("Existe mais de um Protocolo com este ano ({}) e número ({}).".format(ano_protocolo,numero_protocolo)) msg = _( 'Existe mais de um Protocolo com este ano e número.' % ( numero_protocolo, ano_protocolo)) @@ -741,6 +793,8 @@ class DocumentoAdministrativoForm(ModelForm): protocolo__numero=numero_protocolo, protocolo__ano=ano_protocolo).exists() if exist_materia or exist_doc: + self.logger.error('Protocolo com numero=%s e ano=%s já possui' + ' documento vinculado' % (numero_protocolo, ano_protocolo)) raise ValidationError(_('Protocolo %s/%s já possui' ' documento vinculado' % (numero_protocolo, ano_protocolo))) @@ -792,6 +846,8 @@ class DocumentoAdministrativoForm(ModelForm): class DesvincularDocumentoForm(ModelForm): + logger = logging.getLogger(__name__) + numero = forms.CharField(required=True, label=DocumentoAdministrativo._meta. get_field('numero').verbose_name @@ -815,11 +871,15 @@ class DesvincularDocumentoForm(ModelForm): tipo = cleaned_data['tipo'] try: + self.logger.debug("Tentando obter DocumentoAdministrativo com numero={}, ano={} e tipo={}." + .format(numero, ano, tipo)) documento = DocumentoAdministrativo.objects.get(numero=numero, ano=ano, tipo=tipo) if not documento.protocolo: + self.logger.error("DocumentoAdministrativo %s %s/%s não se encontra vinculado a nenhum protocolo." % (tipo, numero, ano)) raise forms.ValidationError( _("%s %s/%s não se encontra vinculado a nenhum protocolo" % (tipo, numero, ano))) except ObjectDoesNotExist: + self.logger.error("DocumentoAdministrativo %s %s/%s não existe" % (tipo, numero, ano)) raise forms.ValidationError( _("%s %s/%s não existe" % (tipo, numero, ano))) @@ -853,6 +913,8 @@ class DesvincularDocumentoForm(ModelForm): class DesvincularMateriaForm(forms.Form): + logger = logging.getLogger(__name__) + numero = forms.CharField(required=True, label=_('Número da Matéria')) ano = forms.ChoiceField(required=True, @@ -877,11 +939,15 @@ class DesvincularMateriaForm(forms.Form): tipo = cleaned_data['tipo'] try: + self.logger.info("Tentando obter MateriaLegislativa com numero={}, ano={} e tipo={}." + .format(numero, ano, tipo)) materia = MateriaLegislativa.objects.get(numero=numero, ano=ano, tipo=tipo) if not materia.numero_protocolo: + self.logger.error("MateriaLegislativa %s %s/%s não se encontra vinculada a nenhum protocolo" % (tipo, numero, ano)) raise forms.ValidationError( _("%s %s/%s não se encontra vinculada a nenhum protocolo" % (tipo, numero, ano))) except ObjectDoesNotExist: + self.logger.error("MateriaLegislativa %s %s/%s não existe" % (tipo, numero, ano)) raise forms.ValidationError( _("%s %s/%s não existe" % (tipo, numero, ano))) diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py old mode 100644 new mode 100755 index 67ea62d5d..87e82cb7c --- a/sapl/protocoloadm/views.py +++ b/sapl/protocoloadm/views.py @@ -21,8 +21,9 @@ from django.views.generic.edit import FormView from django_filters.views import FilterView import sapl -from sapl.base.models import Autor, CasaLegislativa +import logging from sapl.comissoes.models import Comissao +from sapl.base.models import Autor, CasaLegislativa from sapl.crud.base import Crud, CrudAux, MasterDetailCrud, make_pagination from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.parlamentares.models import Legislatura, Parlamentar @@ -59,7 +60,10 @@ def recuperar_materia_protocolo(request): tipo = request.GET.get('tipo') ano = request.GET.get('ano') numero = request.GET.get('numero') + logger = logging.getLogger(__name__) + username = request.user.username try: + logger.debug("user=" + username + ". Tentando obter matéria com tipo={}, ano={} e numero={}.".format(tipo, ano, numero)) materia = MateriaLegislativa.objects.get( tipo=tipo, ano=ano,numero=numero) autoria = materia.autoria_set.first() @@ -70,6 +74,7 @@ def recuperar_materia_protocolo(request): 'tipo_autor':autoria.autor.tipo.pk}) response = JsonResponse(content) except Exception as e: + logger.error("user=" + username + ". " + str(e)) response = JsonResponse({'error':e}) return response @@ -99,7 +104,11 @@ def doc_texto_integral(request, pk): class AcompanhamentoConfirmarView(TemplateView): + logger = logging.getLogger(__name__) + def get_redirect_url(self, email): + username = self.request.user.username + self.logger.info('user=' + username + '. Este documento está sendo acompanhado pelo e-mail: {}'.format(email)) msg = _('Este documento está sendo acompanhado pelo e-mail: %s') % ( email) messages.add_message(self.request, messages.SUCCESS, msg) @@ -109,12 +118,16 @@ class AcompanhamentoConfirmarView(TemplateView): def get(self, request, *args, **kwargs): documento_id = kwargs['pk'] hash_txt = request.GET.get('hash_txt', '') + username = request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter objeto AcompanhamentoDocumento com documento_id={} e hash={}" + .format(documento_id, hash_txt)) acompanhar = AcompanhamentoDocumento.objects.get( documento_id=documento_id, hash=hash_txt) - except ObjectDoesNotExist: + except ObjectDoesNotExist as e: + self.logger.error("user=" + username + ". " + str(e)) raise Http404() # except MultipleObjectsReturned: # A melhor solução deve ser permitir que a exceção @@ -129,21 +142,28 @@ class AcompanhamentoConfirmarView(TemplateView): class AcompanhamentoExcluirView(TemplateView): + logger = logging.getLogger(__name__) + def get_success_url(self): + username = self.request.user.username + self.logger.info("user=" + username + ". Você parou de acompanhar este Documento (pk={}).".format(self.kwargs['pk'])) msg = _('Você parou de acompanhar este Documento.') messages.add_message(self.request, messages.INFO, msg) return reverse('sapl.protocoloadm:documentoadministrativo_detail', kwargs={'pk': self.kwargs['pk']}) def get(self, request, *args, **kwargs): - materia_id = kwargs['pk'] + documento_id = kwargs['pk'] hash_txt = request.GET.get('hash_txt', '') - + username = request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter AcompanhamentoDocumento com documento_id={} e hash={}." + .format(documento_id, hash_txt)) AcompanhamentoDocumento.objects.get(documento_id=documento_id, hash=hash_txt).delete() except ObjectDoesNotExist: - pass + self.logger.error("user=" + username + ". AcompanhamentoDocumento com documento_id={} e hash={} não encontrado." + .format(documento_id, hash_txt)) return HttpResponseRedirect(self.get_success_url()) @@ -151,6 +171,8 @@ class AcompanhamentoExcluirView(TemplateView): class AcompanhamentoDocumentoView(CreateView): template_name = "protocoloadm/acompanhamento_documento.html" + logger = logging.getLogger(__name__) + def get_random_chars(self): s = ascii_letters + digits return ''.join(choice(s) for i in range(choice([6, 7]))) @@ -200,7 +222,9 @@ class AcompanhamentoDocumentoView(CreateView): "documento", documento, destinatario) - + self.logger.info('user={} .Foi enviado um e-mail de confirmação. Confira sua caixa ' + 'de mensagens e clique no link que nós enviamos para ' + 'confirmar o acompanhamento deste documento.'.format(usuario.username)) msg = _('Foi enviado um e-mail de confirmação. Confira sua caixa \ de mensagens e clique no link que nós enviamos para \ confirmar o acompanhamento deste documento.') @@ -209,6 +233,7 @@ class AcompanhamentoDocumentoView(CreateView): # Caso esse Acompanhamento já exista # avisa ao usuário que esse documento já está sendo acompanhado else: + self.logger.info('user=' + request.user.username + '. Este e-mail já está acompanhando esse documento (pk={}).'.format(pk)) msg = _('Este e-mail já está acompanhando esse documento.') messages.add_message(request, messages.INFO, msg) @@ -433,6 +458,9 @@ class AnularProtocoloAdmView(PermissionRequiredMixin, CreateView): class ProtocoloDocumentoView(PermissionRequiredMixin, FormValidMessageMixin, CreateView): + + logger = logging.getLogger(__name__) + template_name = "protocoloadm/protocolar_documento.html" form_class = ProtocoloDocumentForm form_valid_message = _('Protocolo cadastrado com sucesso!') @@ -444,10 +472,14 @@ class ProtocoloDocumentoView(PermissionRequiredMixin, def form_valid(self, form): protocolo = form.save(commit=False) + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter sequência de numeração.") numeracao = sapl.base.models.AppConfig.objects.last( ).sequencia_numeracao - except AttributeError: + except AttributeError as e: + self.logger.error("user=" + username + ". É preciso definir a sequencia de " + "numeração na tabelas auxiliares! " + str(e)) msg = _('É preciso definir a sequencia de ' + 'numeração na tabelas auxiliares!') messages.add_message(self.request, messages.ERROR, msg) @@ -474,7 +506,8 @@ class ProtocoloDocumentoView(PermissionRequiredMixin, if not protocolo.numero: protocolo.numero = (numero['numero__max'] + 1) if numero['numero__max'] else 1 elif protocolo.numero < (numero['numero__max'] + 1) if numero['numero__max'] else 0: - msg = _('Número de protocolo deve ser maior que {}').format(numero['numero__max']) + msg = _('Número de protocolo deve ser maior que {}'.format(numero['numero__max'])) + self.logger.error("user=" + username + ". Número de protocolo deve ser maior que {}.".format(numero['numero__max'])) messages.add_message(self.request, messages.ERROR, msg) return self.render_to_response(self.get_context_data()) protocolo.ano = timezone.now().year @@ -518,21 +551,30 @@ class CriarDocumentoProtocolo(PermissionRequiredMixin, CreateView): class ProtocoloMostrarView(PermissionRequiredMixin, TemplateView): + logger = logging.getLogger(__name__) template_name = "protocoloadm/protocolo_mostrar.html" permission_required = ('protocoloadm.detail_protocolo', ) def get_context_data(self, **kwargs): + context = super(ProtocoloMostrarView, self).get_context_data(**kwargs) protocolo = Protocolo.objects.get(pk=self.kwargs['pk']) + username = self.request.user.username if protocolo.tipo_materia: try: + self.logger.debug("user=" + username + ". Tentando obter objeto MateriaLegislativa com numero_protocolo={} e ano={}." + .format(protocolo.numero, protocolo.ano)) materia = MateriaLegislativa.objects.get( numero_protocolo=protocolo.numero, ano=protocolo.ano) except ObjectDoesNotExist: + self.logger.error("user=" + username + ". Objeto MateriaLegislativa com numero_protocolo={} e ano={} não encontrado." + " Definido como None.".format(protocolo.numero, protocolo.ano)) context['materia'] = None else: + self.logger.info("user=" + username + ". Objeto MateriaLegislativa com numero_protocolo={} e ano={} encontrado" + "com sucesso.".format(protocolo.numero, protocolo.ano)) context['materia'] = materia if protocolo.tipo_documento: @@ -577,6 +619,8 @@ class ComprovanteProtocoloView(PermissionRequiredMixin, TemplateView): class ProtocoloMateriaView(PermissionRequiredMixin, CreateView): + logger = logging.getLogger(__name__) + template_name = "protocoloadm/protocolar_materia.html" form_class = ProtocoloMateriaForm form_valid_message = _('Matéria cadastrada com sucesso!') @@ -588,10 +632,14 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView): def form_valid(self, form): protocolo = form.save(commit=False) + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter sequência de numeração.") numeracao = sapl.base.models.AppConfig.objects.last( ).sequencia_numeracao except AttributeError: + self.logger.error("user=" + username + ". É preciso definir a sequencia de " + "numeração na tabelas auxiliares!") msg = _('É preciso definir a sequencia de ' + 'numeração na tabelas auxiliares!') messages.add_message(self.request, messages.ERROR, msg) @@ -620,6 +668,8 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView): protocolo.numero = (numero['numero__max'] + 1) if numero['numero__max'] else 1 if numero['numero__max']: if protocolo.numero < (numero['numero__max'] + 1): + self.logger.error("user=" + username + ". Número de protocolo ({}) é menor que {}" + .format(protocolo.numero, numero['numero__max'])) msg = _('Número de protocolo deve ser maior que {}').format(numero['numero__max']) messages.add_message(self.request, messages.ERROR, msg) return self.render_to_response(self.get_context_data()) @@ -771,8 +821,14 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin, url = '' self.filterset.form.fields['o'].label = _('Ordenação') - - length = self.object_list.count() + + # é usada essa verificação anônima para quando os documentos administrativos + # estão no modo ostensivo, mas podem existir documentos administrativos restritos + if request.user.is_anonymous(): + length = self.object_list.filter(restrito=False).count() + else: + length = self.object_list.count() + context = self.get_context_data(filter=self.filterset, filter_url=url, numero_res=length @@ -795,6 +851,7 @@ class TramitacaoAdmCrud(MasterDetailCrud): class CreateView(MasterDetailCrud.CreateView): form_class = TramitacaoAdmForm + logger = logging.getLogger(__name__) def get_initial(self): initial = super(CreateView, self).get_initial() @@ -826,30 +883,38 @@ class TramitacaoAdmCrud(MasterDetailCrud): def form_valid(self, form): self.object = form.save() - + username = self.request.user.username try: tramitacao_signal.send(sender=TramitacaoAdministrativo, post=self.object, request=self.request) except Exception as e: # TODO log error + self.logger.error('user=' + username + '. Tramitação criada, mas e-mail de acompanhamento de documento ' + 'não enviado. A não configuração do servidor de e-mail ' + 'impede o envio de aviso de tramitação. ' + str(e)) msg = _('Tramitação criada, mas e-mail de acompanhamento ' - 'de documento não enviado. A não configuração do' - ' servidor de e-mail impede o envio de aviso de tramitação') + 'de documento não enviado. A não configuração do' + ' servidor de e-mail impede o envio de aviso de tramitação') messages.add_message(self.request, messages.WARNING, msg) return HttpResponseRedirect(self.get_success_url()) return super().form_valid(form) class UpdateView(MasterDetailCrud.UpdateView): form_class = TramitacaoAdmEditForm + logger = logging.getLogger(__name__) def form_valid(self, form): self.object = form.save() + username = self.request.user.username try: tramitacao_signal.send(sender=TramitacaoAdministrativo, post=self.object, request=self.request) except Exception as e: # TODO log error + self.logger.error('user=' + username + '. Tramitação criada, mas e-mail de acompanhamento de documento ' + 'não enviado. A não configuração do servidor de e-mail ' + 'impede o envio de aviso de tramitação. ' + str(e)) msg = _('Tramitação criada, mas e-mail de acompanhamento ' 'de documento não enviado. A não configuração do' ' servidor de e-mail impede o envio de aviso de tramitação') diff --git a/sapl/redireciona_urls/views.py b/sapl/redireciona_urls/views.py index ade47c9de..ee0212661 100644 --- a/sapl/redireciona_urls/views.py +++ b/sapl/redireciona_urls/views.py @@ -1,3 +1,5 @@ +import logging + from django.core.urlresolvers import NoReverseMatch, reverse from django.views.generic import RedirectView @@ -71,12 +73,16 @@ def has_iframe(url, request): class RedirecionaSAPLIndex(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): url_pattern = 'sapl_index' + username = self.request.user.username try: + self.logger.info("user=" + username + ". Tentando obter url.") url = reverse(url_pattern) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(url_pattern) url = has_iframe(url, self.request) @@ -86,23 +92,30 @@ class RedirecionaSAPLIndex(RedirectView): class RedirecionaParlamentar(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): + url = EMPTY_STRING pk_parlamentar = self.request.GET.get( 'cod_parlamentar', EMPTY_STRING) + username = self.request.user.username if pk_parlamentar: try: kwargs = {'pk': pk_parlamentar} + self.logger.debug("user=" + username + ". Tentando obter url correspondente.") url = reverse(parlamentar_detail, kwargs=kwargs) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(parlamentar_detail, kwargs=kwargs) else: try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(parlamentar_list) except NoReverseMatch: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(parlamentar_list) numero_legislatura = self.request.GET.get( @@ -119,22 +132,28 @@ class RedirecionaParlamentar(RedirectView): class RedirecionaComissao(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): url = EMPTY_STRING pk_comissao = self.request.GET.get('cod_comissao', EMPTY_STRING) + username = self.request.user.username if pk_comissao: kwargs = {'pk': pk_comissao} try: + self.logger.debug("user=" + username + ". Tentando obter url correspondente.") url = reverse(comissao_detail, kwargs=kwargs) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(comissao_detail) else: try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(comissao_list) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(comissao_list) url = has_iframe(url, self.request) @@ -144,24 +163,30 @@ class RedirecionaComissao(RedirectView): class RedirecionaComposicaoComissao(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): url = EMPTY_STRING pk_composicao = self.request.GET.get( 'cod_periodo_comp_sel', EMPTY_STRING) pk_comissao = self.request.GET.get('cod_comissao', EMPTY_STRING) + username = self.request.user.username if pk_comissao: kwargs = {'pk': pk_comissao} try: + self.logger.debug("user=" + username + ". Tentando obter url correspondente.") url = reverse(comissao_detail, kwargs=kwargs) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(comissao_detail) else: try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(comissao_list) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(comissao_list) url = has_iframe(url, self.request) @@ -171,22 +196,29 @@ class RedirecionaComposicaoComissao(RedirectView): class RedirecionaPautaSessao(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): + pk_sessao_plenaria = self.request.GET.get( 'cod_sessao_plen', EMPTY_STRING) + username = self.request.user.username if pk_sessao_plenaria: kwargs = {'pk': pk_sessao_plenaria} try: + self.logger.debug("user=" + username + ". Tentando obter url correspondente.") url = reverse(pauta_sessao_detail, kwargs=kwargs) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(pauta_sessao_detail) else: try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(pauta_sessao_list) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(pauta_sessao_list) data_sessao_plenaria = self.request.GET.get( @@ -212,23 +244,29 @@ class RedirecionaPautaSessao(RedirectView): class RedirecionaSessaoPlenaria(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): pk_sessao_plenaria = self.request.GET.get( 'cod_sessao_plen', EMPTY_STRING) url = EMPTY_STRING + username = self.request.user.username if pk_sessao_plenaria: kwargs = {'pk': pk_sessao_plenaria} try: + self.logger.debug("user=" + username + ". Tentando obter url correspondente.") url = reverse(sessao_plenaria_detail, kwargs=kwargs) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(sessao_plenaria_detail) else: try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(sessao_plenaria_list) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(sessao_plenaria_list) year = self.request.GET.get( @@ -261,12 +299,17 @@ class RedirecionaSessaoPlenaria(RedirectView): class RedirecionaRelatoriosList(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): + url = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(relatorios_list) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(relatorios_list) url = has_iframe(url, self.request) @@ -276,12 +319,16 @@ class RedirecionaRelatoriosList(RedirectView): class RedirecionaRelatoriosMateriasEmTramitacaoList(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): url = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(relatorio_materia_por_tramitacao) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(relatorio_materia_por_tramitacao) year = self.request.GET.get( @@ -339,13 +386,17 @@ class RedirecionaMateriaLegislativaDetail(RedirectView): class RedirecionaMateriaLegislativaList(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): url = EMPTY_STRING args = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(materialegislativa_list) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(materialegislativa_list) tipo_materia = self.request.GET.get( @@ -414,12 +465,17 @@ class RedirecionaMateriaLegislativaList(RedirectView): class RedirecionaMesaDiretoraView(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): + url = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(parlamentar_mesa_diretora) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(parlamentar_mesa_diretora) url = has_iframe(url, self.request) @@ -447,14 +503,18 @@ class RedirecionaNormasJuridicasDetail(RedirectView): class RedirecionaNormasJuridicasTextoIntegral(RedirectView): permanent = False + logger = logging.getLogger(__name__) def get_redirect_url(self, **kwargs): url = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter NormaJuridica com pk={}.".format(kwargs['norma_id'])) norma = NormaJuridica.objects.get(pk=kwargs['norma_id']) if norma: url = norma.texto_integral.url except Exception as e: + self.logger.error("user=" + username + ". Erro ao obter NormaJuridica com pk={}. ".format(kwargs['norma_id']) + str(e)) raise e url = has_iframe(url, self.request) @@ -465,13 +525,17 @@ class RedirecionaNormasJuridicasTextoIntegral(RedirectView): class RedirecionaNormasJuridicasList(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): url = EMPTY_STRING args = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(norma_juridica_pesquisa) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(norma_juridica_pesquisa) tipo_norma = self.request.GET.get( @@ -523,13 +587,18 @@ class RedirecionaNormasJuridicasList(RedirectView): class RedirecionaHistoricoTramitacoesList(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): + url = EMPTY_STRING args = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(historico_tramitacoes) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(historico_tramitacoes) inicio_intervalo_data_tramitacao = self.request.GET.get( @@ -580,13 +649,18 @@ class RedirecionaHistoricoTramitacoesList(RedirectView): class RedirecionaAtasList(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): + url = EMPTY_STRING args = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(pesquisar_atas) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(pesquisar_atas) inicio_intervalo_data_ata = self.request.GET.get( @@ -614,13 +688,18 @@ class RedirecionaAtasList(RedirectView): class RedirecionaPresencaParlamentares(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): + url = EMPTY_STRING args = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(presenca_sessao) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(presenca_sessao) inicio_intervalo_data_presenca_parlamentar = self.request.GET.get( @@ -648,12 +727,16 @@ class RedirecionaPresencaParlamentares(RedirectView): class RedirecionaMateriasPorAutor(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): url = EMPTY_STRING + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(relatorio_materia_por_autor) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(relatorio_materia_por_autor) url = has_iframe(url, self.request) @@ -664,14 +747,18 @@ class RedirecionaMateriasPorAutor(RedirectView): class RedirecionaMateriasPorAnoAutorTipo(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): + url = EMPTY_STRING ano = self.request.GET.get('ano', '') - + username = self.request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(relatorio_materia_por_ano_autor_tipo) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(relatorio_materia_por_ano_autor_tipo) if ano: @@ -686,23 +773,30 @@ class RedirecionaMateriasPorAnoAutorTipo(RedirectView): class RedirecionaReuniao(RedirectView): permanent = True + logger = logging.getLogger(__name__) def get_redirect_url(self): + pk_reuniao = self.request.GET.get( 'cod_comissao', EMPTY_STRING) url = EMPTY_STRING + username = self.request.user.username if pk_reuniao: kwargs = {'pk': pk_reuniao} try: + self.logger.debug("user=" + username + ". Tentando obter url correspondente (pk={}).".format(kwargs['pk'])) url = reverse(reuniao_detail, kwargs=kwargs) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(reuniao_detail) else: try: + self.logger.debug("user=" + username + ". Tentando obter url.") url = reverse(reuniao_list) - except NoReverseMatch: + except NoReverseMatch as e: + self.logger.error("user=" + username + ". Erro ao obter url. " + str(e)) raise UnknownUrlNameError(reuniao_list) year = self.request.GET.get( diff --git a/sapl/relatorios/templates/pdf_pauta_sessao_gerar.py b/sapl/relatorios/templates/pdf_pauta_sessao_gerar.py index 31636401e..15a4147d6 100755 --- a/sapl/relatorios/templates/pdf_pauta_sessao_gerar.py +++ b/sapl/relatorios/templates/pdf_pauta_sessao_gerar.py @@ -122,8 +122,10 @@ def expediente_materia(lst_expediente_materia): tmp += '\n' tmp += 'MatériaEmentaSituação\n' for expediente_materia in lst_expediente_materia: - tmp += '' + str(expediente_materia['num_ordem']) + ' - ' + expediente_materia[ - 'id_materia'] + '\n' + 'Autor: ' + expediente_materia['nom_autor'] + '\n' + tmp += '' + str(expediente_materia['num_ordem']) + ' - ' + \ + expediente_materia["tipo_materia"] + ' No. ' + \ + expediente_materia['id_materia'] + '\n' + 'Autor: ' + \ + expediente_materia['nom_autor'] + '\n' txt_ementa = expediente_materia['txt_ementa'].replace('&', '&') tmp += '' + txt_ementa + '\n' tmp += '' + \ @@ -145,8 +147,12 @@ def votacao(lst_votacao): tmp += '\n' tmp += 'MatériaEmentaSituação\n' for votacao in lst_votacao: - tmp += '' + str(votacao['num_ordem']) + ' - ' + str(votacao['id_materia']) + '\n' + 'Processo: ' + str(votacao[ - 'des_numeracao']) + '\n' + 'Turno: ' + str(votacao['des_turno']) + '\n' + 'Autor: ' + str(votacao['nom_autor']) + '\n' + tmp += '' + str(votacao['num_ordem']) + ' - ' + \ + votacao["tipo_materia"] + ' No. ' + \ + str(votacao['id_materia']) + '\n' + 'Processo: ' + \ + str(votacao['des_numeracao']) + '\n' + 'Turno: ' + \ + str(votacao['des_turno']) + '\n' + 'Autor: ' + \ + str(votacao['nom_autor']) + '\n' tmp += '' + \ str(votacao['txt_ementa']) + '\n' tmp += '' + \ diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py old mode 100644 new mode 100755 index 2f48f627f..5b04ea82b --- a/sapl/relatorios/views.py +++ b/sapl/relatorios/views.py @@ -1,5 +1,6 @@ import html import re +import logging from datetime import datetime as dt from django.core.exceptions import ObjectDoesNotExist @@ -782,7 +783,8 @@ def relatorio_sessao_plenaria(request, pk): ''' pdf_sessao_plenaria_gerar.py ''' - + logger = logging.getLogger(__name__) + username = request.user.username response = HttpResponse(content_type='application/pdf') response['Content-Disposition'] = ( 'inline; filename="relatorio_protocolo.pdf"') @@ -797,8 +799,10 @@ def relatorio_sessao_plenaria(request, pk): imagem = get_imagem(casa) try: + logger.debug("user=" + username + ". Tentando obter SessaoPlenaria com id={}.".format(pk)) sessao = SessaoPlenaria.objects.get(id=pk) - except ObjectDoesNotExist: + except ObjectDoesNotExist as e: + logger.error("user=" + username + ". Essa SessaoPlenaria não existe (pk={}). ".format(pk) + str(e)) raise Http404('Essa página não existe') (inf_basicas_dic, @@ -1061,6 +1065,7 @@ def get_pauta_sessao(sessao, casa): id=expediente_materia.materia.id).first() dic_expediente_materia = {} + dic_expediente_materia["tipo_materia"] = materia.tipo.sigla + ' - ' + materia.tipo.descricao dic_expediente_materia["num_ordem"] = str( expediente_materia.numero_ordem) dic_expediente_materia["id_materia"] = str( @@ -1113,6 +1118,7 @@ def get_pauta_sessao(sessao, casa): id=votacao.materia.id).first() dic_votacao = {} + dic_votacao["tipo_materia"] = materia.tipo.sigla + ' - ' + materia.tipo.descricao dic_votacao["num_ordem"] = votacao.numero_ordem dic_votacao["id_materia"] = str( materia.numero) + "/" + str(materia.ano) @@ -1123,7 +1129,7 @@ def get_pauta_sessao(sessao, casa): numeracao = Numeracao.objects.filter(materia=votacao.materia).first() if numeracao: - dic_votacao["des_numeracao"] = str( + dic_votacao["des_numeracao"] = str( numeracao.numero_materia) + '/' + str(numeracao.ano_materia) turno, tramitacao = get_turno(dic_votacao, materia, sessao.data_inicio) diff --git a/sapl/rules/apps.py b/sapl/rules/apps.py index dbdfce8ce..522cced6a 100644 --- a/sapl/rules/apps.py +++ b/sapl/rules/apps.py @@ -1,6 +1,8 @@ from builtins import LookupError import django +import logging + from django.apps import apps from django.contrib.auth import get_user_model from django.contrib.auth.management import _get_all_permissions @@ -28,12 +30,14 @@ def create_proxy_permissions( using=DEFAULT_DB_ALIAS, **kwargs): if not app_config.models_module: return - + logger = logging.getLogger(__name__) # print(app_config) try: + logger.info("Tentando obter modelo de permissão do app.") Permission = apps.get_model('auth', 'Permission') - except LookupError: + except LookupError as e: + logger.error(str(e)) return if not router.allow_migrate_model(using, Permission): @@ -69,9 +73,11 @@ def create_proxy_permissions( app_label, model = opts.app_label, opts.model_name try: + logger.info("Tentando obter db_manager.") ctype = ContentType.objects.db_manager( using).get_by_natural_key(app_label, model) - except: + except Exception as e: + logger.error(str(e)) ctype = ContentType.objects.db_manager( using).create(app_label=app_label, model=model) else: @@ -81,12 +87,14 @@ def create_proxy_permissions( # FIXME: Retirar try except quando sapl passar a usar django 1.11 try: + logger.info("_get_all_permissions") # Função não existe mais em Django 1.11 # como sapl ainda não foi para Django 1.11 # esta excessão foi adicionada para caso o # Sapl esteja rodando em um projeto 1.11 não ocorra erros _all_perms_of_klass = _get_all_permissions(klass._meta, ctype) - except: + except Exception as e: + logger.error(str(e)) # Nova função usada em projetos com Django 1.11 e o sapl é uma app _all_perms_of_klass = _get_all_permissions(klass._meta) @@ -111,6 +119,13 @@ def create_proxy_permissions( # error when the name is longer than 255 characters for perm in perms: if len(perm.name) > permission_name_max_length: + logger.error("The permission name %s of %s.%s " + "is longer than %s characters" % ( + perm.name, + perm.content_type.app_label, + perm.content_type.model, + permission_name_max_length, + )) raise exceptions.ValidationError( 'The permission name %s of %s.%s ' 'is longer than %s characters' % ( @@ -155,15 +170,17 @@ def get_rules(): def _config_group(self, group_name, rules_list): if not group_name: return - + logger = logging.getLogger(__name__) group, created = Group.objects.get_or_create(name=group_name) group.permissions.clear() try: + logger.info("Tentando associar grupos.") print(' ', group_name) for model, perms in rules_list: self.associar(group, model, perms) except Exception as e: + logger.error(str(e)) print(group_name, e) def groups_add_user(self, user, groups_name): diff --git a/sapl/rules/map_rules.py b/sapl/rules/map_rules.py index 1a2d1fe8b..342f3383a 100644 --- a/sapl/rules/map_rules.py +++ b/sapl/rules/map_rules.py @@ -66,6 +66,7 @@ rules_group_audiencia = { 'rules': [ (audiencia.AudienciaPublica, __base__), (audiencia.TipoAudienciaPublica, __base__), + (audiencia.AnexoAudienciaPublica, __base__), ] } diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index 96e85b9d8..f514ed6a7 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -430,7 +430,7 @@ class VotacaoForm(forms.Form): class VotacaoNominalForm(forms.Form): resultado_votacao = forms.ModelChoiceField(label='Resultado da Votação', - required=True, + required=False, queryset=TipoResultadoVotacao.objects.all()) diff --git a/sapl/sessao/migrations/0026_auto_20181016_1944.py b/sapl/sessao/migrations/0026_auto_20181016_1944.py new file mode 100644 index 000000000..97ae02603 --- /dev/null +++ b/sapl/sessao/migrations/0026_auto_20181016_1944.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2018-10-16 22:44 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0025_auto_20180919_1116'), + ] + + operations = [ + migrations.AlterField( + model_name='expedientesessao', + name='sessao_plenaria', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sessao.SessaoPlenaria'), + ), + migrations.AlterField( + model_name='integrantemesa', + name='sessao_plenaria', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sessao.SessaoPlenaria'), + ), + migrations.AlterField( + model_name='orador', + name='sessao_plenaria', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sessao.SessaoPlenaria'), + ), + migrations.AlterField( + model_name='oradorexpediente', + name='sessao_plenaria', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sessao.SessaoPlenaria'), + ), + migrations.AlterField( + model_name='presencaordemdia', + name='sessao_plenaria', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sessao.SessaoPlenaria'), + ), + migrations.AlterField( + model_name='registrovotacao', + name='expediente', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sessao.ExpedienteMateria'), + ), + migrations.AlterField( + model_name='registrovotacao', + name='materia', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='materia.MateriaLegislativa'), + ), + migrations.AlterField( + model_name='registrovotacao', + name='ordem', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sessao.OrdemDia'), + ), + migrations.AlterField( + model_name='sessaoplenaria', + name='sessao_legislativa', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parlamentares.SessaoLegislativa', verbose_name='Sessão Legislativa'), + ), + migrations.AlterField( + model_name='sessaoplenariapresenca', + name='sessao_plenaria', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sessao.SessaoPlenaria'), + ), + ] diff --git a/sapl/sessao/migrations/0027_auto_20181023_1239.py b/sapl/sessao/migrations/0027_auto_20181023_1239.py new file mode 100644 index 000000000..962ceaf01 --- /dev/null +++ b/sapl/sessao/migrations/0027_auto_20181023_1239.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.8 on 2018-10-23 15:39 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0026_auto_20181016_1944'), + ] + + operations = [ + migrations.AlterField( + model_name='sessaoplenaria', + name='finalizada', + field=models.NullBooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Sessão finalizada?'), + ), + migrations.AlterField( + model_name='sessaoplenaria', + name='iniciada', + field=models.NullBooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=True, verbose_name='Sessão iniciada?'), + ), + ] diff --git a/sapl/sessao/models.py b/sapl/sessao/models.py index 7f8ea0882..2ef2522e5 100644 --- a/sapl/sessao/models.py +++ b/sapl/sessao/models.py @@ -121,7 +121,7 @@ class SessaoPlenaria(models.Model): verbose_name=_('Tipo')) sessao_legislativa = models.ForeignKey( SessaoLegislativa, - on_delete=models.PROTECT, + on_delete=models.CASCADE, verbose_name=_('Sessão Legislativa')) legislatura = models.ForeignKey(Legislatura, on_delete=models.PROTECT, @@ -160,10 +160,12 @@ class SessaoPlenaria(models.Model): verbose_name=_('Anexo da Sessão')) iniciada = models.NullBooleanField(blank=True, choices=YES_NO_CHOICES, - verbose_name=_('Sessão iniciada?')) + verbose_name=_('Sessão iniciada?'), + default=True) finalizada = models.NullBooleanField(blank=True, choices=YES_NO_CHOICES, - verbose_name=_('Sessão finalizada?')) + verbose_name=_('Sessão finalizada?'), + default=False) interativa = models.NullBooleanField(blank=True, choices=YES_NO_CHOICES, verbose_name=_('Sessão interativa')) @@ -288,7 +290,7 @@ class TipoExpediente(models.Model): @reversion.register() class ExpedienteSessao(models.Model): # ExpedienteSessaoPlenaria sessao_plenaria = models.ForeignKey(SessaoPlenaria, - on_delete=models.PROTECT) + on_delete=models.CASCADE) tipo = models.ForeignKey(TipoExpediente, on_delete=models.PROTECT) conteudo = models.TextField( blank=True, verbose_name=_('Conteúdo do expediente')) @@ -319,7 +321,7 @@ class OcorrenciaSessao(models.Model): # OcorrenciaSessaoPlenaria @reversion.register() class IntegranteMesa(models.Model): # MesaSessaoPlenaria sessao_plenaria = models.ForeignKey(SessaoPlenaria, - on_delete=models.PROTECT) + on_delete=models.CASCADE) cargo = models.ForeignKey(CargoMesa, on_delete=models.PROTECT) parlamentar = models.ForeignKey(Parlamentar, on_delete=models.PROTECT) @@ -334,7 +336,7 @@ class IntegranteMesa(models.Model): # MesaSessaoPlenaria @reversion.register() class AbstractOrador(models.Model): # Oradores sessao_plenaria = models.ForeignKey(SessaoPlenaria, - on_delete=models.PROTECT) + on_delete=models.CASCADE) parlamentar = models.ForeignKey(Parlamentar, on_delete=models.PROTECT, verbose_name=_('Parlamentar')) @@ -387,7 +389,7 @@ class OrdemDia(AbstractOrdemDia): @reversion.register() class PresencaOrdemDia(models.Model): # OrdemDiaPresenca sessao_plenaria = models.ForeignKey(SessaoPlenaria, - on_delete=models.PROTECT) + on_delete=models.CASCADE) parlamentar = models.ForeignKey(Parlamentar, on_delete=models.PROTECT) class Meta: @@ -427,15 +429,15 @@ class RegistroVotacao(models.Model): TipoResultadoVotacao, on_delete=models.PROTECT, verbose_name=_('Resultado da Votação')) - materia = models.ForeignKey(MateriaLegislativa, on_delete=models.PROTECT) + materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE) ordem = models.ForeignKey(OrdemDia, blank=True, null=True, - on_delete=models.PROTECT) + on_delete=models.CASCADE) expediente = models.ForeignKey(ExpedienteMateria, blank=True, null=True, - on_delete=models.PROTECT) + on_delete=models.CASCADE) numero_votos_sim = models.PositiveIntegerField(verbose_name=_('Sim')) numero_votos_nao = models.PositiveIntegerField(verbose_name=_('Não')) numero_abstencoes = models.PositiveIntegerField( @@ -476,7 +478,7 @@ class VotoParlamentar(models.Model): # RegistroVotacaoParlamentar ''' votacao = models.ForeignKey(RegistroVotacao, blank=True, - null=True) + null=True,on_delete=models.CASCADE) parlamentar = models.ForeignKey(Parlamentar, on_delete=models.PROTECT) voto = models.CharField(max_length=10) @@ -496,10 +498,10 @@ class VotoParlamentar(models.Model): # RegistroVotacaoParlamentar ordem = models.ForeignKey(OrdemDia, blank=True, - null=True) + null=True, on_delete=models.CASCADE) expediente = models.ForeignKey(ExpedienteMateria, blank=True, - null=True) + null=True, on_delete=models.CASCADE) class Meta: verbose_name = _('Registro de Votação de Parlamentar') @@ -513,7 +515,7 @@ class VotoParlamentar(models.Model): # RegistroVotacaoParlamentar @reversion.register() class SessaoPlenariaPresenca(models.Model): sessao_plenaria = models.ForeignKey(SessaoPlenaria, - on_delete=models.PROTECT) + on_delete=models.CASCADE) parlamentar = models.ForeignKey(Parlamentar, on_delete=models.PROTECT) data_sessao = models.DateField(blank=True, null=True) diff --git a/sapl/sessao/tests/test_sessao.py b/sapl/sessao/tests/test_sessao.py index c1af4fc7e..3ed1e47f6 100644 --- a/sapl/sessao/tests/test_sessao.py +++ b/sapl/sessao/tests/test_sessao.py @@ -5,11 +5,14 @@ from django.utils.translation import ugettext_lazy as _ from model_mommy import mommy from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa -from sapl.parlamentares.models import Legislatura, Partido, SessaoLegislativa +from sapl.parlamentares.models import Legislatura, Parlamentar, Partido,SessaoLegislativa from sapl.sessao import forms -from sapl.sessao.models import (ExpedienteMateria, OrdemDia, RegistroVotacao, - SessaoPlenaria, TipoSessaoPlenaria) - +from sapl.sessao.models import (ExpedienteMateria, ExpedienteSessao, + IntegranteMesa, Orador, OrdemDia, + PresencaOrdemDia, RegistroVotacao, + SessaoPlenaria, SessaoPlenariaPresenca, + TipoResultadoVotacao, TipoSessaoPlenaria, + VotoParlamentar) def test_valida_campos_obrigatorios_sessao_plenaria_form(): form = forms.SessaoPlenariaForm(data={}) @@ -170,3 +173,124 @@ def test_registro_votacao_tem_ordem_xor_expediente(): # a validação NÃO funciona quando ambos são preenchidos with pytest.raises(ValidationError): registro_votacao_com(ordem, expediente).full_clean() + +def create_sessao_plenaria(): + legislatura = mommy.make(Legislatura) + sessao = mommy.make(SessaoLegislativa) + tipo = mommy.make(TipoSessaoPlenaria) + return mommy.make(SessaoPlenaria, + legislatura=legislatura, + sessao_legislativa=sessao, + tipo=tipo, + numero=1) + +def create_materia_legislativa(): + tipo_materia = mommy.make(TipoMateriaLegislativa) + return mommy.make(MateriaLegislativa, tipo=tipo_materia) + +@pytest.mark.django_db(transaction=False) +def test_delete_sessao_plenaria_cascade_registro_votacao_ordemdia(): + materia = create_materia_legislativa() + sessao_plenaria = create_sessao_plenaria() + ordem = mommy.make(OrdemDia, + sessao_plenaria=sessao_plenaria, + materia=materia, + tipo_votacao='2') + tipo_resultado_votacao = mommy.make(TipoResultadoVotacao, + nome='ok', + natureza="A") + registro = mommy.make(RegistroVotacao, + tipo_resultado_votacao=tipo_resultado_votacao, + materia=materia, + ordem=ordem) + presenca = mommy.make(PresencaOrdemDia, + sessao_plenaria=sessao_plenaria) + parlamentar = mommy.make(Parlamentar) + voto_parlamentar = mommy.make(VotoParlamentar, + votacao=registro, + parlamentar=parlamentar, + ordem=ordem) + sessao_plenaria.delete() + presenca_filter = PresencaOrdemDia.objects.filter( + sessao_plenaria=sessao_plenaria).exists() + ordem_filter = OrdemDia.objects.filter( + sessao_plenaria=sessao_plenaria).exists() + registro_filter = RegistroVotacao.objects.filter( + tipo_resultado_votacao=tipo_resultado_votacao, + materia=materia, + ordem=ordem).exists() + materia_filter = MateriaLegislativa.objects.filter(id=materia.id).exists() + parlamentar_filter = Parlamentar.objects.exists() + voto_parlamentar_filter = VotoParlamentar.objects.filter( + ordem=ordem).exists() + assert not registro_filter + assert not ordem_filter + assert not presenca_filter + assert not voto_parlamentar_filter + assert materia_filter # Não exclui materia + assert parlamentar_filter # Não exclui Parlamentar + +@pytest.mark.django_db(transaction=False) +def test_delete_sessao_plenaria_cascade_registro_votacao_expediente(): + materia = create_materia_legislativa() + sessao_plenaria = create_sessao_plenaria() + expediente = mommy.make(ExpedienteMateria, + sessao_plenaria=sessao_plenaria, + materia=materia, + tipo_votacao='2') + tipo_resultado_votacao = mommy.make(TipoResultadoVotacao, + nome='ok', + natureza="A") + registro = mommy.make(RegistroVotacao, + tipo_resultado_votacao=tipo_resultado_votacao, + materia=materia, + expediente=expediente) + presenca = mommy.make(SessaoPlenariaPresenca, + sessao_plenaria=sessao_plenaria) + parlamentar = mommy.make(Parlamentar) + voto_parlamentar = mommy.make(VotoParlamentar, + votacao=registro, + parlamentar=parlamentar, + expediente=expediente) + sessao_plenaria.delete() + expediente_filter = ExpedienteMateria.objects.filter( + sessao_plenaria=sessao_plenaria).exists() + registro_filter = RegistroVotacao.objects.filter( + tipo_resultado_votacao=tipo_resultado_votacao, + materia=materia, + expediente=expediente).exists() + presenca_filter = SessaoPlenariaPresenca.objects.filter( + sessao_plenaria=sessao_plenaria).exists() + parlamentar_filter = Parlamentar.objects.exists() + voto_parlamentar_filter = VotoParlamentar.objects.filter( + expediente=expediente).exists() + assert not registro_filter + assert not expediente_filter + assert not presenca_filter + assert not voto_parlamentar_filter + assert parlamentar_filter # Não exclui Parlamentar + + +@pytest.mark.django_db(transaction=False) +def test_delete_sessao_plenaria_cascade_integrante_mesa(): + sessao_plenaria = create_sessao_plenaria() + mesa = mommy.make(IntegranteMesa,sessao_plenaria=sessao_plenaria) + sessao_plenaria.delete() + mesa_filter = IntegranteMesa.objects.filter(sessao_plenaria=sessao_plenaria).exists() + assert not mesa_filter + +@pytest.mark.django_db(transaction=False) +def test_delete_sessao_plenaria_cascade_expedientesessao(): + sessao_plenaria = create_sessao_plenaria() + expediente_sessao = mommy.make(ExpedienteSessao, sessao_plenaria=sessao_plenaria) + sessao_plenaria.delete() + expediente_sessao_filter = ExpedienteSessao.objects.filter(sessao_plenaria=sessao_plenaria).exists() + assert not expediente_sessao_filter + +@pytest.mark.django_db(transaction=False) +def test_delete_sessao_plenaria_cascade_orador(): + sessao_plenaria = create_sessao_plenaria() + expediente_sessao = mommy.make(Orador, sessao_plenaria=sessao_plenaria) + sessao_plenaria.delete() + orador_filter = Orador.objects.filter(sessao_plenaria=sessao_plenaria).exists() + assert not orador_filter diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py old mode 100644 new mode 100755 index 6cc9300fb..3c1da0380 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -1,4 +1,5 @@ from operator import itemgetter +import logging from re import sub from django.contrib import messages @@ -62,10 +63,10 @@ TipoResultadoVotacaoCrud = CrudAux.build( def reordernar_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})) @@ -82,7 +83,10 @@ def reordernar_materias_ordem(request, pk): def verifica_presenca(request, model, spk): + logger = logging.getLogger(__name__) if not model.objects.filter(sessao_plenaria_id=spk).exists(): + username = request.user.username + logger.error("user=" + username + ". Votação não pode ser aberta sem presenças (sessao_plenaria_id={}).".format(spk)) msg = _('Votação não pode ser aberta sem presenças') messages.add_message(request, messages.ERROR, msg) return False @@ -94,6 +98,8 @@ def verifica_votacoes_abertas(request): Q(ordemdia__votacao_aberta=True) | Q(expedientemateria__votacao_aberta=True)).distinct() + logger = logging.getLogger(__name__) + if votacoes_abertas: msg_abertas = [] for v in votacoes_abertas: @@ -101,7 +107,10 @@ def verifica_votacoes_abertas(request): reverse('sapl.sessao:sessaoplenaria_detail', kwargs={'pk': v.id}), v.__str__())) - + username = request.user.username + logger.info('user=' + username + '. Já existem votações abertas nas seguintes Sessões: ' + + ', '.join(msg_abertas) + '. Para abrir ' + 'outra, termine ou feche as votações abertas.') msg = _('Já existem votações abertas nas seguintes Sessões: ' + ', '.join(msg_abertas) + '. Para abrir ' 'outra, termine ou feche as votações abertas.') @@ -113,11 +122,16 @@ def verifica_votacoes_abertas(request): def verifica_sessao_iniciada(request, spk): + logger = logging.getLogger(__name__) sessao = SessaoPlenaria.objects.get(id=spk) if not sessao.iniciada or sessao.finalizada: + username = request.user.username + logger.info('user=' + username + '. Não é possível abrir matérias para votação. ' + 'Esta SessaoPlenaria (id={}) não foi iniciada ou está finalizada.'.format(spk)) msg = _('Não é possível abrir matérias para votação. ' - 'Esta Sessão Plenária não foi iniciada ou está finalizada.') + 'Esta Sessão Plenária não foi iniciada ou está finalizada.' + ' Vá em "Abertura"->"Dados Básicos" e altere os valores dos campos necessários.') messages.add_message(request, messages.INFO, msg) return False @@ -571,6 +585,16 @@ class OradorCrud(OradorCrud): return reverse('sapl.sessao:orador_list', kwargs={'pk': self.kwargs['pk']}) + class UpdateView(MasterDetailCrud.UpdateView): + + form_class = OradorForm + + def get_initial(self): + initial = super(UpdateView, self).get_initial() + initial.update({'id_sessao': self.object.sessao_plenaria.id}) + + return initial + class BancadaCrud(Crud): model = Bancada @@ -661,6 +685,7 @@ class SessaoCrud(Crud): class CreateView(Crud.CreateView): form_class = SessaoPlenariaForm + logger = logging.getLogger(__name__) @property def cancel_url(self): @@ -680,6 +705,11 @@ class SessaoCrud(Crud): else: msg = _('Cadastre alguma legislatura antes de adicionar ' + 'uma sessão plenária!') + + username = self.request.user.username + self.logger.error('user=' + username + '. Cadastre alguma legislatura antes de adicionar ' + 'uma sessão plenária!') + messages.add_message(self.request, messages.ERROR, msg) return {} @@ -716,6 +746,7 @@ class PresencaView(FormMixin, PresencaMixin, DetailView): template_name = 'sessao/presenca.html' form_class = PresencaForm model = SessaoPlenaria + logger = logging.getLogger(__name__) def get_context_data(self, **kwargs): context = FormMixin.get_context_data(self, **kwargs) @@ -750,7 +781,8 @@ class PresencaView(FormMixin, PresencaMixin, DetailView): sessao.sessao_plenaria = self.object sessao.parlamentar = Parlamentar.objects.get(id=p) sessao.save() - + username = request.user.username + self.logger.info("user=" + username + ". SessaoPlenariaPresenca salva com sucesso (parlamentar_id={})!".format(p)) msg = _('Presença em Sessão salva com sucesso!') messages.add_message(request, messages.SUCCESS, msg) @@ -766,6 +798,7 @@ class PresencaView(FormMixin, PresencaMixin, DetailView): class PainelView(PermissionRequiredForAppCrudMixin, TemplateView): template_name = 'sessao/painel.html' app_label = 'painel' + logger = logging.getLogger(__name__) def get(self, request, *args, **kwargs): if request.user.is_anonymous(): @@ -787,6 +820,10 @@ class PainelView(PermissionRequiredForAppCrudMixin, TemplateView): if (not cronometro_discurso or not cronometro_aparte or not cronometro_ordem or not cronometro_consideracoes): + + username = self.request.user.username + self.logger.error('user=' + username + '. Você precisa primeiro configurar os cronômetros' + ' nas Configurações da Aplicação') msg = _( 'Você precisa primeiro configurar os cronômetros \ nas Configurações da Aplicação') @@ -823,6 +860,7 @@ class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView): template_name = 'sessao/presenca_ordemdia.html' form_class = PresencaForm model = SessaoPlenaria + logger = logging.getLogger(__name__) def get_context_data(self, **kwargs): context = FormMixin.get_context_data(self, **kwargs) @@ -858,6 +896,8 @@ class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView): ordem.parlamentar = Parlamentar.objects.get(id=p) ordem.save() + username = request.user.username + self.logger.info('user=' + username + '. PresencaOrdemDia (parlamentar com id={}) salva com sucesso!'.format(p)) msg = _('Presença em Ordem do Dia salva com sucesso!') messages.add_message(request, messages.SUCCESS, msg) @@ -984,15 +1024,19 @@ class MesaView(FormMixin, DetailView): template_name = 'sessao/mesa.html' form_class = MesaForm model = SessaoPlenaria + logger = logging.getLogger(__name__) def get(self, request, *args, **kwargs): self.object = self.get_object() context = self.get_context_data(object=self.object) + username = request.user.username try: + self.logger.debug("user=" + username + ". Tentando obter SessaoPlenaria com id={}".format(kwargs['pk'])) sessao = SessaoPlenaria.objects.get( id=kwargs['pk']) except ObjectDoesNotExist: + self.logger.error("user=" + username + ". SessaoPlenaria com id={} não existe.".format(kwargs['pk'])) mensagem = _('Esta Sessão Plenária não existe!') messages.add_message(request, messages.INFO, mensagem) @@ -1044,10 +1088,14 @@ def atualizar_mesa(request): Esta função lida com qualquer alteração nos campos da Mesa Diretora, atualizando os campos após cada alteração """ + logger = logging.getLogger(__name__) + username = request.user.username try: + logger.debug("user=" + username + ". Tentando obter SessaoPlenaria com id={}.".format(request.GET['sessao'])) sessao = SessaoPlenaria.objects.get( id=int(request.GET['sessao'])) except ObjectDoesNotExist: + logger.error("user=" + username + ". SessaoPlenaria com id={} inexistente.".format(request.GET['sessao'])) return JsonResponse({'msg': ('Sessão Inexistente!', 0)}) # Atualiza os componentes da view após a mudança @@ -1086,6 +1134,8 @@ def insere_parlamentar_composicao(request): Esta função lida com qualquer operação de inserção na composição da Mesa Diretora """ + logger = logging.getLogger(__name__) + username = request.user.username if request.user.has_perm( '%s.add_%s' % ( AppConfig.label, IntegranteMesa._meta.model_name)): @@ -1093,15 +1143,19 @@ def insere_parlamentar_composicao(request): composicao = IntegranteMesa() try: + logger.debug("user=" + username + ". Tentando obter SessaoPlenaria com id={}.".format(request.POST['sessao'])) composicao.sessao_plenaria = SessaoPlenaria.objects.get( id=int(request.POST['sessao'])) except MultiValueDictKeyError: + logger.error("user=" + username + ". SessaoPlenaria com id={} não existe.".format(request.POST['sessao'])) return JsonResponse({'msg': ('A Sessão informada não existe!', 0)}) try: + logger.debug("user=" + username + ". Tentando obter Parlamentar com id={}.".format(request.POST['parlamentar'])) composicao.parlamentar = Parlamentar.objects.get( id=int(request.POST['parlamentar'])) except MultiValueDictKeyError: + logger.error("user=" + username + ". Parlamentar com id={} não existe.".format(request.POST['parlamentar'])) return JsonResponse({ 'msg': ('Nenhum parlamentar foi inserido!', 0)}) @@ -1113,13 +1167,17 @@ def insere_parlamentar_composicao(request): cargo_id=composicao.cargo.id).exists() if parlamentar_ja_inserido: + logger.debug("user=" + username + ". Parlamentar (id={}) já inserido na sessao_plenaria(id={}) e cargo(ìd={})." + .format(request.POST['parlamentar'], composicao.sessao_plenaria.id, composicao.cargo.id)) return JsonResponse({'msg': ('Parlamentar já inserido!', 0)}) composicao.save() - except MultiValueDictKeyError: + except MultiValueDictKeyError as e: + logger.error("user=" + username + ". Nenhum cargo foi inserido! " + str(e)) return JsonResponse({'msg': ('Nenhum cargo foi inserido!', 0)}) + logger.info("user=" + username + ". Parlamentar (id={}) inserido com sucesso na sessao_plenaria(id={}) e cargo(ìd={}).") return JsonResponse({'msg': ('Parlamentar inserido com sucesso!', 1)}) else: @@ -1132,23 +1190,30 @@ def remove_parlamentar_composicao(request): Essa função lida com qualquer operação de remoção na composição da Mesa Diretora """ + logger = logging.getLogger(__name__) + username = request.user.username if request.POST and request.user.has_perm( '%s.delete_%s' % ( AppConfig.label, IntegranteMesa._meta.model_name)): if 'composicao_mesa' in request.POST: try: + logger.debug("user=" + username + ". Tentando remover IntegranteMesa com id={}".format(request.POST['composicao_mesa'])) IntegranteMesa.objects.get( id=int(request.POST['composicao_mesa'])).delete() except ObjectDoesNotExist: + logger.error("user=" + username + ". IntegranteMesa com id={} não existe e não pôde ser removido." + .format(request.POST['composicao_mesa'])) return JsonResponse( {'msg': ( 'Composição da Mesa não pôde ser removida!', 0)}) + logger.info("user=" + username + ". IntegranteMesa com id={} removido com sucesso.") return JsonResponse( {'msg': ( 'Parlamentar excluido com sucesso!', 1)}) else: + logger.debug("user=" + username + ". Nenhum parlamentar selecionado para ser excluido!") return JsonResponse( {'msg': ( 'Selecione algum parlamentar para ser excluido!', 0)}) @@ -1470,6 +1535,8 @@ class ResumoView(DetailView): class ResumoAtaView(ResumoView): template_name = 'sessao/resumo_ata.html' + logger = logging.getLogger(__name__) + logger.debug('Gerando Resumo.') class ExpedienteView(FormMixin, DetailView): @@ -1477,6 +1544,8 @@ class ExpedienteView(FormMixin, DetailView): form_class = ExpedienteForm model = SessaoPlenaria + logger = logging.getLogger(__name__) + def get_context_data(self, **kwargs): context = FormMixin.get_context_data(self, **kwargs) context['title'] = '%s (%s)' % ( @@ -1487,10 +1556,12 @@ class ExpedienteView(FormMixin, DetailView): def post(self, request, *args, **kwargs): self.object = self.get_object() form = ExpedienteForm(request.POST) + username = request.user.username if 'apagar-expediente' in request.POST: ExpedienteSessao.objects.filter( sessao_plenaria_id=self.object.id).delete() + self.logger.info('user=' + username + '. ExpedienteSessao de sessao_plenaria_id={} deletado.'.format(self.object.id)) return self.form_valid(form) if form.is_valid(): @@ -1511,9 +1582,13 @@ class ExpedienteView(FormMixin, DetailView): msg = _('Registro salvo com sucesso') messages.add_message(self.request, messages.SUCCESS, msg) + self.logger.info('user=' + username + '. ExpedienteSessao(sessao_plenaria_id={} e tipo_id={}) salvo com sucesso.' + .format(self.object.id, tipo)) + return self.form_valid(form) else: - msg = _('Erro ao salvar registro') + self.logger.error("user=" + username + ". Erro ao salvar registro (sessao_plenaria_id={}).".format(self.object.id)) + msg = _('Erro ao salvar ExpedienteSessao') messages.add_message(self.request, messages.SUCCESS, msg) return self.form_invalid(form) @@ -1557,8 +1632,14 @@ class OcorrenciaSessaoView(FormMixin, DetailView): form_class = OcorrenciaSessaoForm model = SessaoPlenaria + logger = logging.getLogger(__name__) + def delete(self): OcorrenciaSessao.objects.filter(sessao_plenaria=self.object).delete() + + username = self.request.user.username + self.logger.info('user=' + username + '. OcorrenciaSessao com SessaoPlenaria de id={} deletada.' + .format(self.object.id)) msg = _('Registro deletado com sucesso') messages.add_message(self.request, messages.SUCCESS, msg) @@ -1575,6 +1656,9 @@ class OcorrenciaSessaoView(FormMixin, DetailView): msg = _('Registro salvo com sucesso') messages.add_message(self.request, messages.SUCCESS, msg) + + username = self.request.user.username + self.logger.info('user=' + username + '. OcorrenciaSessao de sessao_plenaria_id={} atualizada com sucesso.'.format(self.object.id)) @method_decorator(permission_required('sessao.add_ocorrenciasessao')) def post(self, request, *args, **kwargs): @@ -1588,7 +1672,7 @@ class OcorrenciaSessaoView(FormMixin, DetailView): self.delete() elif request.POST.get('save'): - self.save(form) + self.save(form) return self.form_valid(form) @@ -1677,6 +1761,8 @@ class VotacaoView(SessaoPermissionMixin): template_name = 'sessao/votacao/votacao.html' form_class = VotacaoForm + logger = logging.getLogger(__name__) + def get(self, request, *args, **kwargs): self.object = self.get_object() context = self.get_context_data(object=self.object) @@ -1761,7 +1847,10 @@ class VotacaoView(SessaoPermissionMixin): votacao.tipo_resultado_votacao_id = int( request.POST['resultado_votacao']) votacao.save() - except: + except Exception as e: + username = request.user.username + self.logger.error('user=' + username + '. Problemas ao salvar RegistroVotacao da materia de id={} ' + 'e da ordem de id={}. '.format(materia_id, ordem_id) + str(e)) return self.form_invalid(form) else: ordem = OrdemDia.objects.get( @@ -1809,18 +1898,23 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): expediente = None form_class = VotacaoNominalForm + logger = logging.getLogger(__name__) + def get(self, request, *args, **kwargs): + username = request.user.username if self.ordem: ordem_id = kwargs['oid'] if RegistroVotacao.objects.filter(ordem_id=ordem_id).exists(): msg = _('Esta matéria já foi votada!') messages.add_message(request, messages.ERROR, msg) + self.logger.info('user=' + username + '. Matéria (ordem_id={}) já votada!'.format(ordem_id)) return HttpResponseRedirect(reverse( 'sapl.sessao:ordemdia_list', kwargs={'pk': kwargs['pk']})) try: ordem = OrdemDia.objects.get(id=ordem_id) except ObjectDoesNotExist: + self.logger.error('user=' + username + '. Objeto OrdemDia (pk={}) não existe.'.format(ordem_id)) raise Http404() presentes = PresencaOrdemDia.objects.filter( @@ -1830,6 +1924,7 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): materia_votacao = ordem if not ordem.votacao_aberta: + self.logger.error('user=' + username + '. A votação para esta OrdemDia (id={}) encontra-se fechada!'.format(ordem_id)) msg = _('A votação para esta matéria encontra-se fechada!') messages.add_message(request, messages.ERROR, msg) return HttpResponseRedirect(reverse( @@ -1842,6 +1937,7 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): expediente_id = kwargs['oid'] if (RegistroVotacao.objects.filter( expediente_id=expediente_id).exists()): + self.logger.error("user=" + username + ". RegistroVotacao (expediente_id={}) já existe.".format(expediente_id)) msg = _('Esta matéria já foi votada!') messages.add_message(request, messages.ERROR, msg) return HttpResponseRedirect(reverse( @@ -1849,8 +1945,10 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): kwargs={'pk': kwargs['pk']})) try: + self.logger.debug("user=" + username + ". Tentando obter Objeto ExpedienteMateria com id={}.".format(expediente_id)) expediente = ExpedienteMateria.objects.get(id=expediente_id) except ObjectDoesNotExist: + self.logger.error('user=' + username + '. Objeto ExpedienteMateria com id={} não existe.'.format(expediente_id)) raise Http404() presentes = SessaoPlenariaPresenca.objects.filter( @@ -1860,7 +1958,7 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): materia_votacao = expediente if not expediente.votacao_aberta: - msg = _('A votação para esta matéria encontra-se fechada!') + msg = _('A votação para este ExpedienteMateria (id={}) encontra-se fechada!'.format(expediente_id)) messages.add_message(request, messages.ERROR, msg) return HttpResponseRedirect(reverse( 'sapl.sessao:expedientemateria_list', @@ -1883,24 +1981,26 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): def post(self, request, *args, **kwargs): self.object = self.get_object() form = self.get_form() + username = request.user.username if self.ordem: ordem_id = kwargs['oid'] try: + self.logger.debug("user=" + username + ". Tentando obter objeto OrdemDia com id={}.".format(ordem_id)) materia_votacao = OrdemDia.objects.get(id=ordem_id) except ObjectDoesNotExist: + self.logger.error('user=' + username + '. Objeto OrdemDia com id={} não existe.'.format(ordem_id)) raise Http404() elif self.expediente: expediente_id = kwargs['oid'] try: + self.logger.debug("user=" + username + ". Tentando obter ExpedienteMateria com id={}.".format(expediente_id)) materia_votacao = ExpedienteMateria.objects.get( id=expediente_id) except ObjectDoesNotExist: + self.logger.error('user=' + username + '. Objeto ExpedienteMateria com id={} não existe.'.format(expediente_id)) raise Http404() - if 'cancelar-votacao' in request.POST: - fechar_votacao_materia(materia_votacao) - return self.form_valid(form) if form.is_valid(): votos_sim = 0 @@ -1908,6 +2008,21 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): abstencoes = 0 nao_votou = 0 + if 'cancelar-votacao' in request.POST: + fechar_votacao_materia(materia_votacao) + if self.ordem: + return HttpResponseRedirect(reverse( + 'sapl.sessao:ordemdia_list', kwargs={'pk': kwargs['pk']})) + else: + return HttpResponseRedirect(reverse( + 'sapl.sessao:expedientemateria_list', + kwargs={'pk': kwargs['pk']})) + else: + if form.cleaned_data['resultado_votacao'] == None: + form.add_error(None, 'Não é possível finalizar a votação sem ' + 'nenhum resultado da votação') + return self.form_invalid(form) + for votos in request.POST.getlist('voto_parlamentar'): v = votos.split(':') voto = v[0] @@ -1924,6 +2039,8 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): # Caso todas as opções sejam 'Não votou', fecha a votação if nao_votou == len(request.POST.getlist('voto_parlamentar')): + self.logger.error('user=' + username + '. Não é possível finalizar a votação sem ' + 'nenhum voto') form.add_error(None, 'Não é possível finalizar a votação sem ' 'nenhum voto') return self.form_invalid(form) @@ -2034,6 +2151,9 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): voto = voto_parlamentar.get( parlamentar=parlamentar) except ObjectDoesNotExist: + username = self.request.user.username + self.logger.error('user=' + username + '. Objeto voto_parlamentar do ' + + 'parlamentar de id={} não existe.'.format(parlamentar.pk)) yield [parlamentar, None] else: yield [parlamentar, voto.voto] @@ -2052,8 +2172,11 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): class VotacaoNominalEditAbstract(SessaoPermissionMixin): template_name = 'sessao/votacao/nominal_edit.html' + logger = logging.getLogger(__name__) + def get(self, request, *args, **kwargs): context = {} + username = request.user.username if self.ordem: ordem_id = kwargs['oid'] @@ -2062,6 +2185,7 @@ class VotacaoNominalEditAbstract(SessaoPermissionMixin): votacao = RegistroVotacao.objects.filter(ordem_id=ordem_id).last() if not ordem or not votacao: + self.logger.error('user=' + username + '. Objeto OrdemDia com id={} ou RegistroVotacao de OrdemDia não existe.'.format(ordem_id)) raise Http404() materia = ordem.materia @@ -2076,6 +2200,8 @@ class VotacaoNominalEditAbstract(SessaoPermissionMixin): expediente_id=expediente_id).last() if not expediente or not votacao: + self.logger.error('user=' + username + '. Objeto ExpedienteMateria com id={} ou RegistroVotacao de ' + + 'ExpedienteMateria não existe.'.format(expediente_id)) raise Http404() materia = expediente.materia @@ -2124,6 +2250,7 @@ class VotacaoNominalEditAbstract(SessaoPermissionMixin): def post(self, request, *args, **kwargs): self.object = self.get_object() form = VotacaoEditForm(request.POST) + username = request.user.username if self.ordem: ordem_id = kwargs['oid'] @@ -2131,6 +2258,7 @@ class VotacaoNominalEditAbstract(SessaoPermissionMixin): try: materia_votacao = OrdemDia.objects.get(id=ordem_id) except ObjectDoesNotExist: + self.logger.error('user=' + username + '. Objeto OrdemDia com id={} não existe.'.format(ordem_id)) raise Http404() elif self.expediente: @@ -2140,6 +2268,7 @@ class VotacaoNominalEditAbstract(SessaoPermissionMixin): materia_votacao = ExpedienteMateria.objects.get( id=expediente_id) except ObjectDoesNotExist: + self.logger.error('user=' + username + '. Objeto ExpedienteMateria com id={} não existe.'.format(expediente_id)) raise Http404() if(int(request.POST['anular_votacao']) == 1): @@ -2315,6 +2444,7 @@ class VotacaoExpedienteView(SessaoPermissionMixin): template_name = 'sessao/votacao/votacao.html' form_class = VotacaoForm + logger = logging.getLogger(__name__) def get(self, request, *args, **kwargs): self.object = self.get_object() @@ -2402,7 +2532,9 @@ class VotacaoExpedienteView(SessaoPermissionMixin): votacao.tipo_resultado_votacao_id = int( request.POST['resultado_votacao']) votacao.save() - except: + except Exception as e: + username = request.user.username + self.logger.error("user=" + username + ". " + str(e)) return self.form_invalid(form) else: expediente = ExpedienteMateria.objects.get( @@ -2671,6 +2803,8 @@ class PesquisarSessaoPlenariaView(FilterView): filterset_class = SessaoPlenariaFilterSet paginate_by = 10 + logger = logging.getLogger(__name__) + def get_filterset_kwargs(self, filterset_class): super(PesquisarSessaoPlenariaView, self).get_filterset_kwargs(filterset_class) @@ -2726,6 +2860,9 @@ class PesquisarSessaoPlenariaView(FilterView): context['show_results'] = show_results_filter_set( self.request.GET.copy()) + username = request.user.username + self.logger.debug('user=' + username + '. Pesquisa de SessaoPlenaria.') + return self.render_to_response(context) @@ -2733,6 +2870,9 @@ class PesquisarPautaSessaoView(PesquisarSessaoPlenariaView): filterset_class = PautaSessaoFilterSet template_name = 'sessao/pauta_sessao_filter.html' + logger = logging.getLogger(__name__) + logger.debug('Pesquisa de PautaSessao.') + def get_context_data(self, **kwargs): context = super(PesquisarPautaSessaoView, self).get_context_data(**kwargs) @@ -2753,6 +2893,8 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin, template_name = 'sessao/adicionar_varias_materias_expediente.html' app_label = AppConfig.label + logger = logging.getLogger(__name__) + def get_filterset_kwargs(self, filterset_class): super(AdicionarVariasMateriasExpediente, self).get_filterset_kwargs(filterset_class) @@ -2795,6 +2937,7 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin, def post(self, request, *args, **kwargs): marcadas = request.POST.getlist('materia_id') + username = request.user.username for m in marcadas: try: @@ -2802,11 +2945,14 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin, msg = _('%s adicionado(a) com sucesso!' % MateriaLegislativa.objects.get(id=m)) messages.add_message(request, messages.SUCCESS, msg) + self.logger.info("user=" + username + ". MateriaLegislativa de id={} adicionado(a) com sucesso!".format(m)) except MultiValueDictKeyError: msg = _('Formulário Inválido. Você esqueceu de selecionar ' + - 'o tipo de votação de %s' % + '%s' % MateriaLegislativa.objects.get(id=m)) messages.add_message(request, messages.ERROR, msg) + self.logger.error("user=" + username + '. Formulário Inválido. Você esqueceu de ' + + 'selecionar o tipo de votação de MateriaLegislativa de id={}.'.format(m)) return self.get(request, self.kwargs) if tipo_votacao: @@ -2838,6 +2984,8 @@ class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente): filterset_class = AdicionarVariasMateriasFilterSet template_name = 'sessao/adicionar_varias_materias_ordem.html' + logger = logging.getLogger(__name__) + def get_filterset_kwargs(self, filterset_class): super(AdicionarVariasMateriasExpediente, self).get_filterset_kwargs(filterset_class) @@ -2864,6 +3012,7 @@ class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente): def post(self, request, *args, **kwargs): marcadas = request.POST.getlist('materia_id') + username = request.user.username for m in marcadas: try: @@ -2871,11 +3020,15 @@ class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente): msg = _('%s adicionado(a) com sucesso!' % MateriaLegislativa.objects.get(id=m)) messages.add_message(request, messages.SUCCESS, msg) + self.logger.debug('user=' + username + '. MateriaLegislativa de id={} adicionado(a) com sucesso!'.format(m)) except MultiValueDictKeyError: msg = _('Formulário Inválido. Você esqueceu de selecionar ' + 'o tipo de votação de %s' % MateriaLegislativa.objects.get(id=m)) messages.add_message(request, messages.ERROR, msg) + self.logger.error('user=' + username + '. Formulário Inválido. Você esqueceu de selecionar ' + 'o tipo de votação de MateriaLegislativa com id={}'.format(m)) + return self.get(request, self.kwargs) if tipo_votacao: @@ -2909,6 +3062,7 @@ def mudar_ordem_materia_sessao(request): posicao_inicial = int(request.POST['pos_ini']) + 1 posicao_final = int(request.POST['pos_fim']) + 1 pk_sessao = int(request.POST['pk_sessao']) + logger = logging.getLogger(__name__) materia = request.POST['materia'] @@ -2926,6 +3080,8 @@ def mudar_ordem_materia_sessao(request): sessao_plenaria=pk_sessao, numero_ordem=posicao_inicial) except ObjectDoesNotExist: + username = request.user.username + logger.error("user=" + username + ". Materia com sessao_plenaria={} e numero_ordem={}.".format(pk_sessao, posicao_inicial)) raise # TODO tratar essa exceção # Se a posição inicial for menor que a final, todos que diff --git a/sapl/settings.py b/sapl/settings.py old mode 100644 new mode 100755 index 7413ecb81..7e382fc4e --- a/sapl/settings.py +++ b/sapl/settings.py @@ -13,6 +13,8 @@ Quick-start development settings - unsuitable for production See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ """ +import socket +import logging from decouple import config from dj_database_url import parse as db_url @@ -22,10 +24,11 @@ from unipath import Path from .temp_suppress_crispy_form_warnings import \ SUPRESS_CRISPY_FORM_WARNINGS_LOGGING +host = socket.gethostbyname_ex(socket.gethostname())[0] + BASE_DIR = Path(__file__).ancestor(1) PROJECT_DIR = Path(__file__).ancestor(2) - # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = config('SECRET_KEY', default='') # SECURITY WARNING: don't run with debug turned on in production! @@ -115,11 +118,7 @@ HAYSTACK_CONNECTIONS = { }, } - -if DEBUG: - INSTALLED_APPS += ('debug_toolbar', 'rest_framework_docs',) - -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = [ 'reversion.middleware.RevisionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', @@ -130,8 +129,13 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', - # 'speedinfo.middleware.ProfilerMiddleware', # Bug na versão 1.9 -) + 'speedinfo.middleware.ProfilerMiddleware', +] +if DEBUG: + INSTALLED_APPS += ('debug_toolbar', 'rest_framework_docs',) + MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware', ] + INTERNAL_IPS = ('127.0.0.1') + CACHES = { 'default': { @@ -292,34 +296,40 @@ SASS_PROCESSOR_INCLUDE_DIRS = (BOWER_COMPONENTS_ROOT.child( # suprime texto de ajuda default do django-filter FILTERS_HELP_TEXT_FILTER = False - -# FIXME update cripy-forms and remove this -# hack to suppress many annoying warnings from crispy_forms -# see sapl.temp_suppress_crispy_form_warnings -LOGGING = SUPRESS_CRISPY_FORM_WARNINGS_LOGGING - - -# FIXME: gerando problemas na alternancia entre django 1.9.13 e 1.10.8 -# Issue 52 https://github.com/interlegis/sapl/issues/52 -LOGGING_CONSOLE = config('LOGGING_CONSOLE', default=False, cast=bool) -"""if DEBUG and LOGGING_CONSOLE: - # Descomentar linha abaixo fará com que logs aparecam, inclusive SQL - # LOGGING['handlers']['console']['level'] = 'DEBUG' - LOGGING['loggers']['django']['level'] = 'DEBUG' - LOGGING['formatters'].update({ +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { 'verbose': { - 'format': '%(levelname)s %(asctime)s %(pathname)s ' - '%(funcName)s %(message)s' + 'format': '%(levelname)s %(asctime)s ' + host + ' %(pathname)s %(name)s:%(funcName)s:%(lineno)d %(message)s' }, 'simple': { - 'format': '%(levelname)s %(message)s' + 'format': '%(levelname)s %(asctime)s - %(message)s' + }, + }, + 'handlers': { + 'console': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'formatter': 'simple', + }, + 'applogfile': { + 'level':'INFO', + 'class':'logging.handlers.RotatingFileHandler', + 'filename': 'sapl.log', + 'maxBytes': 1024*1024*15, # 15MB + 'backupCount': 10, + 'formatter': 'verbose', + }, + }, + 'loggers': { + 'sapl': { + 'handlers': ['applogfile'], + 'level': 'INFO', + 'propagate': True, }, - }) - LOGGING['handlers']['console']['formatter'] = 'verbose' - LOGGING['loggers'][BASE_DIR.name] = { - 'handlers': ['console'], - 'level': 'DEBUG', } +} def excepthook(*args): diff --git a/sapl/static/styles/app.scss b/sapl/static/styles/app.scss index 099392fde..7f50c9339 100644 --- a/sapl/static/styles/app.scss +++ b/sapl/static/styles/app.scss @@ -168,6 +168,7 @@ fieldset { img.img-responsive { height: 95px; margin-right: $navbar-padding-horizontal; + display: inline-block; } small { color: #93A4AA; diff --git a/sapl/templates/404.html b/sapl/templates/404.html new file mode 100644 index 000000000..484a2a8f2 --- /dev/null +++ b/sapl/templates/404.html @@ -0,0 +1,171 @@ +{% load i18n staticfiles sass_tags menus %} +{% load common_tags %} + + + + + + + + + {% block head_title %}{% trans 'SAPL - Sistema de Apoio ao Processo Legislativo' %}{% endblock %} + + {% block head_content %} + + + {# Styles #} + + + + + + + + + {# Scripts #} + {# modernizr must be in head (see http://modernizr.com/docs/#installing) #} + {% endblock %} + + + +
    + + {% if not request|has_iframe %} + {% block navigation %} + + {% endblock navigation %} + + {# Header #} + {% block main_header %} +
    + +
    + {% endblock main_header %} + {% else %} +
    + +
    +
    +
    +
    + {% subnav %} +
    +
    +
    + {% endif %} +
    + +

    {% trans 'Página não encontrada! Erro 404' %}

    +
    +
    + {% block base_content %} + {% endblock %} + {% if not request|has_iframe %} + {% block footer_container %} + +
    + {% endblock footer_container %} + {% endif %} + + {% block foot_js %} + + + + + + + + + + + + + + + + + + + + + {% block extra_js %}{% endblock %} + + + + {% endblock foot_js %} + + diff --git a/sapl/templates/500.html b/sapl/templates/500.html new file mode 100644 index 000000000..ab1afae4f --- /dev/null +++ b/sapl/templates/500.html @@ -0,0 +1,135 @@ +{% load i18n staticfiles sass_tags menus %} +{% load common_tags %} + + + + + + + + + {% block head_title %}{% trans 'SAPL - Sistema de Apoio ao Processo Legislativo' %}{% endblock %} + + {% block head_content %} + + + {# Styles #} + + + + + + + + + {# Scripts #} + {# modernizr must be in head (see http://modernizr.com/docs/#installing) #} + {% endblock %} + + + +
    + + {% block navigation %} + + {% endblock navigation %} + + {# Header #} + {% block main_header %} +
    +
    + +
    + {% block sections_nav %} {% subnav %} {% endblock sections_nav %} +
    +
    +
    + {% endblock main_header %} + +
    + +

    {% trans 'Ocorreu um erro inesperado! Erro 500' %}

    +
    +
    + {% block base_content %} + {% endblock %} + {% block footer_container %} + +
    + {% endblock footer_container %} + + {% block foot_js %} + + + + + + + + + + + + + + + + + + + + + {% block extra_js %}{% endblock %} + + {% endblock foot_js %} + + diff --git a/sapl/templates/audiencia/layouts.yaml b/sapl/templates/audiencia/layouts.yaml index 90433db63..8e93b9f3a 100644 --- a/sapl/templates/audiencia/layouts.yaml +++ b/sapl/templates/audiencia/layouts.yaml @@ -21,4 +21,9 @@ AudienciaPublicaDetail: - url_audio url_video - upload_pauta upload_ata upload_anexo - observacao - - audiencia_cancelada \ No newline at end of file + - audiencia_cancelada + +AnexoAudienciaPublica: + {% trans 'Documento Acessório' %}: + - assunto + - arquivo \ No newline at end of file diff --git a/sapl/templates/audiencia/subnav.yaml b/sapl/templates/audiencia/subnav.yaml new file mode 100644 index 000000000..e6dce83bf --- /dev/null +++ b/sapl/templates/audiencia/subnav.yaml @@ -0,0 +1,5 @@ +{% load i18n common_tags %} +- title: {% trans 'Início' %} + url: audienciapublica_detail +- title: {% trans 'Documento Acessório' %} + url: anexoaudienciapublica_list \ No newline at end of file diff --git a/sapl/templates/base.html b/sapl/templates/base.html index c7cfd8b69..4b247db09 100644 --- a/sapl/templates/base.html +++ b/sapl/templates/base.html @@ -1,6 +1,6 @@ + {% load i18n staticfiles sass_tags menus %} {% load common_tags %} - @@ -90,7 +90,7 @@
    diff --git a/sapl/templates/compilacao/dispositivo_form_search_fragment.html b/sapl/templates/compilacao/dispositivo_form_search_fragment.html index 95d91a2cf..2df5aa0de 100644 --- a/sapl/templates/compilacao/dispositivo_form_search_fragment.html +++ b/sapl/templates/compilacao/dispositivo_form_search_fragment.html @@ -1,5 +1,14 @@ {% load i18n compilacao_filters %} +{% for message in messages %} + +{% endfor %} + {% if object_list.count >= 100 %}
    {% trans 'Use argumentos para simplificar listagem...' %} diff --git a/sapl/templates/compilacao/messages.html b/sapl/templates/compilacao/messages.html new file mode 100644 index 000000000..1777831c7 --- /dev/null +++ b/sapl/templates/compilacao/messages.html @@ -0,0 +1,10 @@ +{% load i18n compilacao_filters %} + +{% for message in messages %} + +{% endfor %} \ No newline at end of file diff --git a/sapl/templates/crud/detail.html b/sapl/templates/crud/detail.html index 4a1cad276..c1b2c6445 100644 --- a/sapl/templates/crud/detail.html +++ b/sapl/templates/crud/detail.html @@ -65,7 +65,21 @@

    {{ column.verbose_name }}

    - {% if column.text|url %} + {% if column.text|audio_url %} +
    + +
    + {% elif column.text|video_url %} +
    + +
    + {% elif column.text|url %} {% else %}
    {{ column.text|safe|default:"" }}
    diff --git a/sapl/templates/materia/impressos/ficha_pdf.html b/sapl/templates/materia/impressos/ficha_pdf.html index 07ad4092a..faa7163e2 100644 --- a/sapl/templates/materia/impressos/ficha_pdf.html +++ b/sapl/templates/materia/impressos/ficha_pdf.html @@ -70,7 +70,7 @@ body PROCESSO Nº {{ materia.numeracao_set.first.numero_materia }} / {{ materia.numeracao_set.first.ano_materia }}
      {% else %} - PROCESSO Nº: {{ materia.numero }}
    + PROCESSO Nº: {{ materia.numero }} / {{materia.ano}}
      {% endif %}
    @@ -96,6 +96,14 @@ body Autor: {{materia.autoria_set.first.autor.nome}}
    + {% if materia.numero_protocolo%} + + +
    +
    + Protocolo: {{materia.numero_protocolo}} / {{materia.ano}}
    +
    + {% endif %} diff --git a/sapl/templates/materia/materialegislativa_detail.html b/sapl/templates/materia/materialegislativa_detail.html index 0a5f06259..f2c986fd5 100644 --- a/sapl/templates/materia/materialegislativa_detail.html +++ b/sapl/templates/materia/materialegislativa_detail.html @@ -10,6 +10,21 @@ {% endblock sub_actions %} {% block detail_content %} {{ block.super }} + {% if object.registrovotacao_set.exists %} + Data Votação: + {% for rv in object.registrovotacao_set.all %} + {% if rv.ordem %} + + {{ rv.ordem.sessao_plenaria.data_inicio }} + + {% elif rv.expediente %} + + {{ rv.expediente.sessao_plenaria.data_inicio }} + + {% endif %} +
    + {% endfor %} + {% endif %} {% if object.normajuridica_set.last %}

      Norma Jurídica Relacionada

    diff --git a/sapl/templates/materia/proposicao_detail.html b/sapl/templates/materia/proposicao_detail.html index 1c9b7df14..9ab80ab62 100644 --- a/sapl/templates/materia/proposicao_detail.html +++ b/sapl/templates/materia/proposicao_detail.html @@ -143,14 +143,31 @@

    {% trans "Vínculo com a Matéria Legislativa" %}

    + Matéria
    +      {{object.materia_de_vinculo}} -
    +
    + {% if object.materia_de_vinculo.autoria_set.all %} + Autores + {% for a in object.materia_de_vinculo.autoria_set.all %} +
        {{a.autor}} + {% endfor %} + {% endif %} +
    + Texto Original +
    +      + + {{object.materia_de_vinculo.texto_original| to_str | split:"/" | get_last_item_from_list:-1}} + +
    + {% endif %} - + {% if not AppConfig.receber_recibo_proposicao %} {% if object.hash_code %} diff --git a/sapl/templates/protocoloadm/protocolo_mostrar.html b/sapl/templates/protocoloadm/protocolo_mostrar.html index c45c70bec..09884f582 100644 --- a/sapl/templates/protocoloadm/protocolo_mostrar.html +++ b/sapl/templates/protocoloadm/protocolo_mostrar.html @@ -9,10 +9,10 @@ Etiqueta Individual
    Assunto: {{ protocolo.assunto_ementa|default:"Não informado" }}
    - {% if p.timestamp%} - Data Protocolo: {{ p.timestamp|localtime|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ p.timestamp|localtime|date:"G:i:s" }}
    + {% if protocolo.timestamp %} + Data Protocolo: {{ protocolo.timestamp|localtime|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ protocolo.timestamp|localtime|date:"G:i:s" }}
    {% else %} - Data Protocolo: {{ p.data|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ p.hora|date:"G:i:s" }}
    + Data Protocolo: {{ protocolo.data|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ protocolo.hora|date:"G:i:s" }}
    {% endif %} {% if protocolo.tipo_processo == 0 %} diff --git a/sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html b/sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html index b1385e7c2..9b65733f9 100644 --- a/sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html +++ b/sapl/templates/sessao/blocos_ata/ocorrencias_da_sessao.html @@ -1,6 +1,6 @@

    Ocorrências da Sessão: - {{object.ocorrenciasessao.conteudo|safe}} + {{object.ocorrenciasessao.conteudo|striptags|safe}}

    diff --git a/sapl/test_urls.py b/sapl/test_urls.py index 851b1995b..23e0c9544 100644 --- a/sapl/test_urls.py +++ b/sapl/test_urls.py @@ -56,7 +56,7 @@ def create_perms_post_migrate(sapl_app_config): ctype = ContentType.objects.get_for_model(klass) ctypes.add(ctype) - for perm in _get_all_permissions(klass._meta, ctype): + for perm in _get_all_permissions(klass._meta): searched_perms.append((ctype, perm)) all_perms = set(Permission.objects.filter( diff --git a/sapl/urls.py b/sapl/urls.py index 07f382013..904b4357e 100644 --- a/sapl/urls.py +++ b/sapl/urls.py @@ -70,6 +70,12 @@ urlpatterns = [ # http://stackoverflow.com/questions/35510373/ if settings.DEBUG: + import debug_toolbar + + urlpatterns += [ + url(r'^__debug__/', include(debug_toolbar.urls)), + + ] urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/sapl/utils.py b/sapl/utils.py index 257489321..5c8d6e097 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -28,6 +28,10 @@ from unipath.path import Path from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row +# (26/10/2018): O separador foi mudador de '/' para 'K' +# por conta dos leitores de códigos de barra, que trocavam +# a '/' por '&' ou ';' +SEPARADOR_HASH_PROPOSICAO = 'K' def pil_image(source, exif_orientation=False, **options): return source_generators.pil_image(source, exif_orientation, **options) @@ -484,7 +488,7 @@ def gerar_hash_arquivo(arquivo, pk, block_size=2**20): if not data: break md5.update(data) - return 'P' + md5.hexdigest() + '/' + pk + return 'P' + md5.hexdigest() + SEPARADOR_HASH_PROPOSICAO + pk class ChoiceWithoutValidationField(forms.ChoiceField): diff --git a/setup.py b/setup.py index 0b7838be2..44a27b522 100644 --- a/setup.py +++ b/setup.py @@ -11,29 +11,30 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) install_requires = [ 'dj-database-url==0.4.1', 'django-haystack==2.6.0', - 'django>=1.9,<1.10', + 'django>=1.10,<1.11', # TODO O django-admin-bootstrapped 2.5.7 não inseriu a mudança que permite # a compatibilidade com Django 1.9+. A linha abaixo será mudada quando uma # nova versão do django-admin-bootstrapped for lançada # 'git+git://github.com/django-admin-bootstrapped/ # django-admin-bootstrapped.git', 'django-bootstrap3==7.0.1', - 'django-bower==5.1.0', + 'django-bower==5.2.0', 'django-braces==1.9.0', 'django-compressor==2.0', - 'django-crispy-forms==1.6.0', - 'django-extensions==1.6.7', - 'django-extra-views==0.8.0', + 'django-crispy-forms==1.6.1', + 'django-extensions==1.9.8', + 'django-extra-views==0.11.0', 'django-filter==0.15.3', 'django-floppyforms==1.6.2', - 'django-model-utils==2.5', - 'django-sass-processor==0.5.4', - 'djangorestframework', + 'django-model-utils==3.1.1', + 'django-sass-processor==0.5.8', + 'djangorestframework==3.4.0', 'drfdocs', - 'easy-thumbnails==2.3', + 'easy-thumbnails==2.5', + 'django-image-cropping==1.1.0', # 'git+git://github.com/interlegis/trml2pdf.git', 'libsass==0.11.1', - 'psycopg2==2.7.3', + 'psycopg2==2.7.4', 'python-decouple==3.0', 'pytz==2016.4', 'pyyaml==3.11', @@ -45,11 +46,13 @@ install_requires = [ 'gunicorn==19.6.0', 'django-reversion==2.0.8', 'WeasyPrint==0.42', - 'whoosh==2.7.4' + 'whoosh==2.7.4', + 'django-speedinfo==1.3.5', + 'django-reversion-compare==0.8.4' ] setup( name='interlegis-sapl', - version='3.1.124', + version='3.1.131', packages=find_packages(), include_package_data=True, license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007',