Browse Source

Merge branch '3.1.x' into fix-relatorios

pull/2433/head
Cesar Augusto de Carvalho 7 years ago
committed by GitHub
parent
commit
011a197de8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      config/env-sample
  2. 1
      config/env_dockerfile
  3. 3
      docker-compose.yml
  4. 1
      docs/instalacao31.rst
  5. 54
      requirements/requirements.txt
  6. 1
      sapl/.env_test
  7. 2
      sapl/api/forms.py
  8. 3
      sapl/api/views.py
  9. 2
      sapl/base/email_utils.py
  10. 13
      sapl/base/templatetags/common_tags.py
  11. 11
      sapl/crud/base.py
  12. 1
      sapl/env-backup
  13. 52
      sapl/materia/forms.py
  14. 19
      sapl/materia/views.py
  15. 18
      sapl/norma/views.py
  16. 104
      sapl/parlamentares/views.py
  17. 155
      sapl/protocoloadm/forms.py
  18. 35
      sapl/protocoloadm/migrations/0014_auto_20190110_1300.py
  19. 21
      sapl/protocoloadm/migrations/0015_protocolo_timestamp_data_hora_manual.py
  20. 21
      sapl/protocoloadm/migrations/0016_auto_20190110_1345.py
  21. 24
      sapl/protocoloadm/models.py
  22. 23
      sapl/protocoloadm/tests/test_protocoloadm.py
  23. 39
      sapl/protocoloadm/views.py
  24. 33
      sapl/relatorios/views.py
  25. 8
      sapl/sessao/forms.py
  26. 47
      sapl/settings.py
  27. 3
      sapl/static/js/app.js
  28. 3
      sapl/static/styles/app.scss
  29. 22
      sapl/temp_suppress_crispy_form_warnings.py
  30. 2
      sapl/templates/base.html
  31. 4
      sapl/templates/compilacao/tipotextoarticulado_list.html
  32. 4
      sapl/templates/crud/list_tabaux.html
  33. 22
      sapl/templates/parlamentares/frente_form.html
  34. 6
      sapl/templates/parlamentares/parlamentares_list.html
  35. 6
      sapl/templates/protocoloadm/comprovante.html
  36. 14
      sapl/templates/protocoloadm/protocolar_documento.html
  37. 62
      sapl/templates/protocoloadm/protocolar_materia.html
  38. 2
      sapl/templates/protocoloadm/protocolo_filter.html
  39. 10
      sapl/templates/protocoloadm/protocolo_mostrar.html
  40. 55
      sapl/utils.py
  41. 58
      setup.py

1
config/env-sample

@ -5,4 +5,5 @@ EMAIL_USE_TLS = True
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_HOST = '' EMAIL_HOST = ''
EMAIL_HOST_USER = '' EMAIL_HOST_USER = ''
EMAIL_SEND_USER = ''
EMAIL_HOST_PASSWORD = '' EMAIL_HOST_PASSWORD = ''

1
config/env_dockerfile

@ -5,4 +5,5 @@ EMAIL_USE_TLS = True
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_HOST = '' EMAIL_HOST = ''
EMAIL_HOST_USER = '' EMAIL_HOST_USER = ''
EMAIL_SEND_USER = ''
EMAIL_HOST_PASSWORD = '' EMAIL_HOST_PASSWORD = ''

3
docker-compose.yml

@ -11,7 +11,7 @@ sapldb:
ports: ports:
- "5432:5432" - "5432:5432"
sapl: sapl:
image: interlegis/sapl:3.1.140 image: interlegis/sapl:3.1.142
restart: always restart: always
environment: environment:
ADMIN_PASSWORD: interlegis ADMIN_PASSWORD: interlegis
@ -21,6 +21,7 @@ sapl:
EMAIL_USE_TLS: 'False' EMAIL_USE_TLS: 'False'
EMAIL_HOST: smtp.dominio.net EMAIL_HOST: smtp.dominio.net
EMAIL_HOST_USER: usuariosmtp EMAIL_HOST_USER: usuariosmtp
EMAIL_SEND_USER: usuariosmtp
EMAIL_HOST_PASSWORD: senhasmtp EMAIL_HOST_PASSWORD: senhasmtp
TZ: America/Sao_Paulo TZ: America/Sao_Paulo
volumes: volumes:

1
docs/instalacao31.rst

@ -147,6 +147,7 @@ Criação da `SECRET_KEY <https://docs.djangoproject.com/es/1.9/ref/settings/#st
EMAIL_PORT = [Insira este parâmetro] EMAIL_PORT = [Insira este parâmetro]
EMAIL_HOST = [Insira este parâmetro] EMAIL_HOST = [Insira este parâmetro]
EMAIL_HOST_USER = [Insira este parâmetro] EMAIL_HOST_USER = [Insira este parâmetro]
EMAIL_SEND_USER = [Insira este parâmetro]
EMAIL_HOST_PASSWORD = [Insira este parâmetro] EMAIL_HOST_PASSWORD = [Insira este parâmetro]
DEFAULT_FROM_EMAIL = [Insira este parâmetro] DEFAULT_FROM_EMAIL = [Insira este parâmetro]
SERVER_EMAIL = [Insira este parâmetro] SERVER_EMAIL = [Insira este parâmetro]

54
requirements/requirements.txt

@ -1,37 +1,39 @@
dj-database-url==0.4.1 django>=1.11,<2.0
django-haystack==2.6.0 django-bootstrap3==11.0.0
django>=1.10,<1.11 django-haystack==2.8.1
django-bootstrap3==7.0.1 django-filter==2.0.0
djangorestframework==3.9.0
dj-database-url==0.5.0
django-bower==5.2.0 django-bower==5.2.0
django-braces==1.9.0 django-braces==1.9.0
django-compressor==2.0 django-crispy-forms==1.7.2
django-crispy-forms==1.6.1 django-floppyforms==1.7.0
django-extensions==1.9.8 django-extra-views==0.12.0
django-extra-views==0.11.0 django-model-utils==3.1.2
django-filter==1.0.0 django-sass-processor==0.7.2
django-floppyforms==1.6.2 django-reversion==3.0.2
django-model-utils==3.1.1 django-reversion-compare==0.8.6
django-sass-processor==0.5.8 django-speedinfo==1.4.0
djangorestframework==3.4.0 django-extensions==2.1.4
easy-thumbnails==2.5
django-image-cropping==1.2 django-image-cropping==1.2
libsass==0.11.1 easy-thumbnails==2.5
psycopg2-binary==2.7.4 libsass==0.17.0
python-decouple==3.0 python-decouple==3.1
pytz==2016.4 psycopg2-binary==2.7.6.1
pyyaml==4.2b1 pyyaml==4.2b1
rtyaml==0.0.3 pytz==2018.9
textract==1.5.0 rtyaml==0.0.5
python-magic==0.4.15
unipath==1.1 unipath==1.1
WeasyPrint==44
gunicorn==19.9.0
textract==1.5.0
pysolr==3.6.0 pysolr==3.6.0
python-magic==0.4.12
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
git+git://github.com/interlegis/trml2pdf.git git+git://github.com/interlegis/trml2pdf.git
git+git://github.com/jasperlittle/django-rest-framework-docs git+git://github.com/jasperlittle/django-rest-framework-docs
git+git://github.com/rubgombar1/django-admin-bootstrapped.git git+git://github.com/rubgombar1/django-admin-bootstrapped.git
#django-compressor==2.2

1
sapl/.env_test

@ -5,4 +5,5 @@ EMAIL_USE_TLS = True
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_HOST = '' EMAIL_HOST = ''
EMAIL_HOST_USER = '' EMAIL_HOST_USER = ''
EMAIL_SEND_USER = ''
EMAIL_HOST_PASSWORD = '' EMAIL_HOST_PASSWORD = ''

2
sapl/api/forms.py

@ -6,8 +6,8 @@ from django.forms.widgets import MultiWidget, TextInput
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django_filters.filters import CharFilter, ModelChoiceFilter, DateFilter from django_filters.filters import CharFilter, ModelChoiceFilter, DateFilter
from django_filters.rest_framework.filterset import FilterSet
from rest_framework import serializers from rest_framework import serializers
from rest_framework.filters import FilterSet
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.parlamentares.models import Legislatura from sapl.parlamentares.models import Legislatura

3
sapl/api/views.py

@ -1,9 +1,10 @@
import logging import logging
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Q from django.db.models import Q
from django.http import Http404 from django.http import Http404
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.filters import DjangoFilterBackend from django_filters.rest_framework.backends import DjangoFilterBackend
from rest_framework.generics import ListAPIView from rest_framework.generics import ListAPIView
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import (AllowAny, IsAuthenticated, from rest_framework.permissions import (AllowAny, IsAuthenticated,

2
sapl/base/email_utils.py

@ -18,7 +18,7 @@ def load_email_templates(templates, context={}):
emails = [] emails = []
for t in templates: for t in templates:
tpl = loader.get_template(t) tpl = loader.get_template(t)
email = tpl.render(Context(context)) email = tpl.render(context)
if t.endswith(".html"): if t.endswith(".html"):
email = email.replace('\n', '').replace('\r', '') email = email.replace('\n', '').replace('\r', '')
emails.append(email) emails.append(email)

13
sapl/base/templatetags/common_tags.py

@ -1,8 +1,4 @@
import logging
from compressor.utils import get_class
from django import template from django import template
from django.conf import settings
from django.template.defaultfilters import stringfilter from django.template.defaultfilters import stringfilter
from sapl.base.models import AppConfig from sapl.base.models import AppConfig
@ -15,6 +11,15 @@ from sapl.utils import filiacao_data, SEPARADOR_HASH_PROPOSICAO
register = template.Library() register = template.Library()
def get_class(class_string):
if not hasattr(class_string, '__bases__'):
class_string = str(class_string)
dot = class_string.rindex('.')
mod_name, class_name = class_string[:dot], class_string[dot + 1:]
if class_name:
return getattr(__import__(mod_name, {}, {}, [str('')]), class_name)
@register.simple_tag @register.simple_tag
def define(arg): def define(arg):
return arg return arg

11
sapl/crud/base.py

@ -1,6 +1,6 @@
import logging import logging
from braces.views import FormMessagesMixin from braces.views import FormMessagesMixin
from compressor.utils.decorators import cached_property
from crispy_forms.bootstrap import FieldWithButtons, StrictButton from crispy_forms.bootstrap import FieldWithButtons, StrictButton
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Field, Layout from crispy_forms.layout import Field, Layout
@ -16,6 +16,7 @@ from django.http.response import Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.decorators import classonlymethod from django.utils.decorators import classonlymethod
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.functional import cached_property
from django.utils.translation import string_concat from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DeleteView, DetailView, ListView, from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
@ -29,6 +30,7 @@ from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL,
from sapl.settings import BASE_DIR from sapl.settings import BASE_DIR
from sapl.utils import normalize from sapl.utils import normalize
ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \ ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \
'list', 'create', 'detail', 'update', 'delete' 'list', 'create', 'detail', 'update', 'delete'
@ -558,7 +560,8 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
fm = model._meta.get_field(fo) fm = model._meta.get_field(fo)
except Exception as e: except Exception as e:
username = self.request.user.username username = self.request.user.username
self.logger.error("user=" + username + ". " + str(e)) self.logger.error(
"user=" + username + ". " + str(e))
pass pass
if fm and hasattr(fm, 'related_model')\ if fm and hasattr(fm, 'related_model')\
@ -882,12 +885,12 @@ class CrudDeleteView(PermissionRequiredContainerCrudMixin,
error_msg2 += '{} - {}, '.format( error_msg2 += '{} - {}, '.format(
i._meta.verbose_name, i i._meta.verbose_name, i
) )
error_msg2 = error_msg2[:len(error_msg2)-2] + '.' error_msg2 = error_msg2[:len(error_msg2) - 2] + '.'
error_msg += '</ul>' error_msg += '</ul>'
username = request.user.username username = request.user.username
self.logger.error("user=" + username + ". Registro não pode ser removido, pois " self.logger.error("user=" + username + ". Registro não pode ser removido, pois "
"é referenciado por outros registros: " + error_msg2) "é referenciado por outros registros: " + error_msg2)
messages.add_message(request, messages.add_message(request,
messages.ERROR, messages.ERROR,
error_msg) error_msg)

1
sapl/env-backup

@ -5,4 +5,5 @@ EMAIL_USE_TLS = True
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_HOST = '' EMAIL_HOST = ''
EMAIL_HOST_USER = '' EMAIL_HOST_USER = ''
EMAIL_SEND_USER = ''
EMAIL_HOST_PASSWORD = '' EMAIL_HOST_PASSWORD = ''

52
sapl/materia/forms.py

@ -1298,41 +1298,14 @@ class TipoProposicaoForm(ModelForm):
class TipoProposicaoSelect(Select): class TipoProposicaoSelect(Select):
def render_tipo_option(self, selected_choices, option_value, option_label, def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
data_has_perfil=False): option = super().create_option(name, value, label, selected,
if option_value is None: index, subindex=subindex, attrs=attrs)
option_value = '' if value:
option_value = force_text(option_value) tipo = TipoProposicao.objects.get(id=value)
if option_value in selected_choices: option['attrs']['data-has-perfil'] = str(tipo.perfis.exists())
selected_html = mark_safe(' selected="selected"')
if not self.allow_multiple_selected: return option
# Only allow for a single selection.
selected_choices.remove(option_value)
else:
selected_html = ''
return format_html(
'<option value="{}"{} data-has-perfil={}>{}</option>',
option_value,
selected_html,
str(data_has_perfil),
force_text(option_label))
def render_options(self, selected_choices):
# Normalize to strings.
selected_choices = set(force_text(v) for v in selected_choices)
output = []
output.append(
self.render_tipo_option(
selected_choices, '', self.choices.field.empty_label))
for tipo in self.choices.queryset.all():
output.append(
self.render_tipo_option(
selected_choices,
str(tipo.pk),
str(tipo),
data_has_perfil=tipo.perfis.exists()))
return '\n'.join(output)
class ProposicaoForm(forms.ModelForm): class ProposicaoForm(forms.ModelForm):
@ -2046,15 +2019,10 @@ class ConfirmarProposicaoForm(ProposicaoForm):
self.instance.results['messages']['success'].append(_( self.instance.results['messages']['success'].append(_(
'Protocolo realizado com sucesso')) 'Protocolo realizado com sucesso'))
# FIXME qdo protocoloadm estiver homologado, verifique a necessidade
# de redirecionamento para o protocolo.
# complete e libere código abaixo para tal.
"""
self.instance.results['url'] = reverse( self.instance.results['url'] = reverse(
'sapl.protocoloadm:...', 'sapl.protocoloadm:protocolo_mostrar',
kwargs={'pk': protocolo.pk}) kwargs={'pk': protocolo.pk})
"""
conteudo_gerado.numero_protocolo = protocolo.numero conteudo_gerado.numero_protocolo = protocolo.numero
conteudo_gerado.save() conteudo_gerado.save()

19
sapl/materia/views.py

@ -26,7 +26,7 @@ import weasyprint
import sapl import sapl
from sapl.base.email_utils import do_envia_email_confirmacao from sapl.base.email_utils import do_envia_email_confirmacao
from sapl.base.models import Autor, CasaLegislativa from sapl.base.models import Autor, CasaLegislativa, AppConfig as BaseAppConfig
from sapl.base.signals import tramitacao_signal from sapl.base.signals import tramitacao_signal
from sapl.comissoes.models import Comissao, Participacao from sapl.comissoes.models import Comissao, Participacao
from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_RESTRICT, from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_RESTRICT,
@ -783,16 +783,23 @@ class ProposicaoCrud(Crud):
msg_error = _('Proposição não possui nenhum tipo de ' msg_error = _('Proposição não possui nenhum tipo de '
'Texto associado.') 'Texto associado.')
else: else:
p.data_devolucao = None
p.data_envio = timezone.now()
p.save()
if p.texto_articulado.exists(): if p.texto_articulado.exists():
ta = p.texto_articulado.first() ta = p.texto_articulado.first()
ta.privacidade = STATUS_TA_IMMUTABLE_RESTRICT ta.privacidade = STATUS_TA_IMMUTABLE_RESTRICT
ta.editing_locked = True ta.editing_locked = True
ta.save() ta.save()
receber_recibo = BaseAppConfig.attr(
'receber_recibo_proposicao')
if not receber_recibo:
ta = p.texto_articulado.first()
p.hash_code = 'P' + ta.hash() + SEPARADOR_HASH_PROPOSICAO + str(p.pk)
p.data_devolucao = None
p.data_envio = timezone.now()
p.save()
messages.success(request, _( messages.success(request, _(
'Proposição enviada com sucesso.')) 'Proposição enviada com sucesso.'))
try: try:
@ -2168,7 +2175,7 @@ class ImpressosView(PermissionRequiredMixin, TemplateView):
def gerar_pdf_impressos(request, context, template_name): def gerar_pdf_impressos(request, context, template_name):
template = loader.get_template(template_name) template = loader.get_template(template_name)
html = template.render(RequestContext(request, context)) html = template.render(context, request)
pdf = weasyprint.HTML(string=html, base_url=request.build_absolute_uri() pdf = weasyprint.HTML(string=html, base_url=request.build_absolute_uri()
).write_pdf() ).write_pdf()

18
sapl/norma/views.py

@ -1,7 +1,5 @@
import logging import logging
import re import re
import sapl
import weasyprint
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
@ -14,7 +12,10 @@ from django.views.generic import TemplateView, UpdateView
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django_filters.views import FilterView from django_filters.views import FilterView
import weasyprint
from sapl import settings from sapl import settings
import sapl
from sapl.base.models import AppConfig from sapl.base.models import AppConfig
from sapl.compilacao.views import IntegracaoTaView from sapl.compilacao.views import IntegracaoTaView
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux, from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
@ -107,7 +108,8 @@ class NormaPesquisaView(FilterView):
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['show_results'] = show_results_filter_set(qr) context['show_results'] = show_results_filter_set(qr)
context['USE_SOLR'] = settings.USE_SOLR if hasattr(settings, 'USE_SOLR') else False context['USE_SOLR'] = settings.USE_SOLR if hasattr(
settings, 'USE_SOLR') else False
return context return context
@ -200,7 +202,6 @@ class NormaCrud(Crud):
horario_acesso=timezone.now()) horario_acesso=timezone.now())
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
class DeleteView(Crud.DeleteView): class DeleteView(Crud.DeleteView):
def get_success_url(self): def get_success_url(self):
@ -219,12 +220,14 @@ class NormaCrud(Crud):
username = self.request.user.username username = self.request.user.username
try: try:
self.logger.debug('user=' + username + '. Tentando obter objeto de modelo da esfera da federação.') self.logger.debug(
'user=' + username + '. Tentando obter objeto de modelo da esfera da federação.')
esfera = sapl.base.models.AppConfig.objects.last( esfera = sapl.base.models.AppConfig.objects.last(
).esfera_federacao ).esfera_federacao
self.initial['esfera_federacao'] = esfera self.initial['esfera_federacao'] = esfera
except: except:
self.logger.error('user=' + username + '. Erro ao obter objeto de modelo da esfera da federação.') self.logger.error(
'user=' + username + '. Erro ao obter objeto de modelo da esfera da federação.')
pass pass
self.initial['complemento'] = False self.initial['complemento'] = False
return self.initial return self.initial
@ -333,6 +336,7 @@ class AutoriaNormaCrud(MasterDetailCrud):
}) })
return initial return initial
class ImpressosView(PermissionRequiredMixin, TemplateView): class ImpressosView(PermissionRequiredMixin, TemplateView):
template_name = 'materia/impressos/impressos.html' template_name = 'materia/impressos/impressos.html'
permission_required = ('materia.can_access_impressos', ) permission_required = ('materia.can_access_impressos', )
@ -340,7 +344,7 @@ class ImpressosView(PermissionRequiredMixin, TemplateView):
def gerar_pdf_impressos(request, context, template_name): def gerar_pdf_impressos(request, context, template_name):
template = loader.get_template(template_name) template = loader.get_template(template_name)
html = template.render(RequestContext(request, context)) html = template.render(context, request)
pdf = weasyprint.HTML(string=html, base_url=request.build_absolute_uri() pdf = weasyprint.HTML(string=html, base_url=request.build_absolute_uri()
).write_pdf() ).write_pdf()

104
sapl/parlamentares/views.py

@ -1,6 +1,6 @@
from datetime import datetime
import json import json
import logging import logging
from datetime import datetime
from django.contrib import messages from django.contrib import messages
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -35,6 +35,7 @@ from .models import (CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa,
NivelInstrucao, Parlamentar, Partido, SessaoLegislativa, NivelInstrucao, Parlamentar, Partido, SessaoLegislativa,
SituacaoMilitar, TipoAfastamento, TipoDependente, Votante) SituacaoMilitar, TipoAfastamento, TipoDependente, Votante)
CargoMesaCrud = CrudAux.build(CargoMesa, 'cargo_mesa') CargoMesaCrud = CrudAux.build(CargoMesa, 'cargo_mesa')
PartidoCrud = CrudAux.build(Partido, 'partidos') PartidoCrud = CrudAux.build(Partido, 'partidos')
TipoDependenteCrud = CrudAux.build(TipoDependente, 'tipo_dependente') TipoDependenteCrud = CrudAux.build(TipoDependente, 'tipo_dependente')
@ -45,6 +46,7 @@ TipoMilitarCrud = CrudAux.build(SituacaoMilitar, 'tipo_situa_militar')
DependenteCrud = MasterDetailCrud.build( DependenteCrud = MasterDetailCrud.build(
Dependente, 'parlamentar', 'dependente') Dependente, 'parlamentar', 'dependente')
class SessaoLegislativaCrud(CrudAux): class SessaoLegislativaCrud(CrudAux):
model = SessaoLegislativa model = SessaoLegislativa
@ -54,6 +56,7 @@ class SessaoLegislativaCrud(CrudAux):
class UpdateView(CrudAux.UpdateView): class UpdateView(CrudAux.UpdateView):
form_class = SessaoLegislativaForm form_class = SessaoLegislativaForm
class VotanteView(MasterDetailCrud): class VotanteView(MasterDetailCrud):
model = Votante model = Votante
parent_field = 'parlamentar' parent_field = 'parlamentar'
@ -89,6 +92,7 @@ class FrenteList(MasterDetailCrud):
class BaseMixin(Crud.PublicMixin, MasterDetailCrud.BaseMixin): class BaseMixin(Crud.PublicMixin, MasterDetailCrud.BaseMixin):
list_field_names = ['nome', 'data_criacao', 'data_extincao'] list_field_names = ['nome', 'data_criacao', 'data_extincao']
@classmethod @classmethod
def url_name(cls, suffix): def url_name(cls, suffix):
return '%s_parlamentar_%s' % (cls.model._meta.model_name, suffix) return '%s_parlamentar_%s' % (cls.model._meta.model_name, suffix)
@ -276,13 +280,16 @@ def parlamentares_frente_selected(request):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
username = request.user.username username = request.user.username
try: try:
logger.info("user=" + username + ". Tentando objet objeto Frente com id={}.".format(request.GET['frente_id'])) 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'])) frente = Frente.objects.get(id=int(request.GET['frente_id']))
except ObjectDoesNotExist: except ObjectDoesNotExist:
logger.error("user=" + username + ". Frente buscada (id={}) não existe. Retornada lista vazia.".format(request.GET['frente_id'])) logger.error("user=" + username +
". Frente buscada (id={}) não existe. Retornada lista vazia.".format(request.GET['frente_id']))
lista_parlamentar_id = [] lista_parlamentar_id = []
else: else:
logger.info("user=" + username + ". Frente (id={}) encontrada com sucesso.".format(request.GET['frente_id'])) logger.info("user=" + username +
". Frente (id={}) encontrada com sucesso.".format(request.GET['frente_id']))
lista_parlamentar_id = frente.parlamentares.all().values_list( lista_parlamentar_id = frente.parlamentares.all().values_list(
'id', flat=True) 'id', flat=True)
return JsonResponse({'id_list': list(lista_parlamentar_id)}) return JsonResponse({'id_list': list(lista_parlamentar_id)})
@ -292,8 +299,14 @@ class FrenteCrud(Crud):
model = Frente model = Frente
help_topic = 'tipo_situa_militar' help_topic = 'tipo_situa_militar'
public = [RP_DETAIL, RP_LIST] public = [RP_DETAIL, RP_LIST]
list_field_names = ['nome', 'data_criacao', 'data_extincao', 'parlamentares'] list_field_names = ['nome', 'data_criacao',
'data_extincao', 'parlamentares']
class BaseMixin(Crud.BaseMixin):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['subnav_template_name'] = ''
return context
class CreateView(Crud.CreateView): class CreateView(Crud.CreateView):
form_class = FrenteForm form_class = FrenteForm
@ -305,7 +318,6 @@ class FrenteCrud(Crud):
form_class = FrenteForm form_class = FrenteForm
class MandatoCrud(MasterDetailCrud): class MandatoCrud(MasterDetailCrud):
model = Mandato model = Mandato
parent_field = 'parlamentar' parent_field = 'parlamentar'
@ -371,11 +383,13 @@ class LegislaturaCrud(CrudAux):
def get_initial(self): def get_initial(self):
username = self.request.user.username username = self.request.user.username
try: try:
self.logger.error("user=" + username + ". Tentando obter última Legislatura.") self.logger.error("user=" + username +
". Tentando obter última Legislatura.")
ultima_legislatura = Legislatura.objects.latest('numero') ultima_legislatura = Legislatura.objects.latest('numero')
numero = ultima_legislatura.numero + 1 numero = ultima_legislatura.numero + 1
except Legislatura.DoesNotExist: except Legislatura.DoesNotExist:
self.logger.error("user=" + username + ". Legislatura não encontrada. Número definido como 1.") self.logger.error(
"user=" + username + ". Legislatura não encontrada. Número definido como 1.")
numero = 1 numero = 1
return {'numero': numero} return {'numero': numero}
@ -476,10 +490,12 @@ class ParlamentarCrud(Crud):
def take_legislatura_id(self): def take_legislatura_id(self):
username = self.request.user.username username = self.request.user.username
try: try:
self.logger.debug("user=" + username + ". Tentando obter id da legislatura.") self.logger.debug("user=" + username +
". Tentando obter id da legislatura.")
return int(self.request.GET['pk']) return int(self.request.GET['pk'])
except: except:
self.logger.error("user=" + username + ". Legislatura não possui ID. Buscando em todas as entradas.") self.logger.error(
"user=" + username + ". Legislatura não possui ID. Buscando em todas as entradas.")
legislaturas = Legislatura.objects.all() legislaturas = Legislatura.objects.all()
for l in legislaturas: for l in legislaturas:
if l.atual(): if l.atual():
@ -501,14 +517,17 @@ class ParlamentarCrud(Crud):
mandato_titular=F('mandato__titular')).distinct() mandato_titular=F('mandato__titular')).distinct()
else: else:
try: try:
self.logger.debug("user=" + username + ". Tentando obter o mais recente registro do objeto Legislatura.") self.logger.debug(
"user=" + username + ". Tentando obter o mais recente registro do objeto Legislatura.")
l = Legislatura.objects.all().order_by( l = Legislatura.objects.all().order_by(
'-data_inicio').first() '-data_inicio').first()
except ObjectDoesNotExist: except ObjectDoesNotExist:
self.logger.error("user=" + username + ". Objeto não encontrado. Retornando todos os registros.") self.logger.error(
"user=" + username + ". Objeto não encontrado. Retornando todos os registros.")
return Legislatura.objects.all() return Legislatura.objects.all()
else: else:
self.logger.info("user=" + username + ". Objeto encontrado com sucesso.") self.logger.info("user=" + username +
". Objeto encontrado com sucesso.")
if l is None: if l is None:
return Legislatura.objects.all() return Legislatura.objects.all()
return queryset.filter(mandato__legislatura_id=l).annotate( return queryset.filter(mandato__legislatura_id=l).annotate(
@ -547,8 +566,8 @@ class ParlamentarCrud(Crud):
# ou igual a data de fim da legislatura # ou igual a data de fim da legislatura
try: try:
self.logger.debug("user=" + username + ". Tentando obter filiação do parlamentar com (data<={} e data_desfiliacao>={}) " self.logger.debug("user=" + username + ". Tentando obter filiação do parlamentar com (data<={} e data_desfiliacao>={}) "
"ou (data<={} e data_desfiliacao=Null))." "ou (data<={} e data_desfiliacao=Null))."
.format(legislatura.data_fim, legislatura.data_fim, legislatura.data_fim)) .format(legislatura.data_fim, legislatura.data_fim, legislatura.data_fim))
filiacao = parlamentar.filiacao_set.get(Q( filiacao = parlamentar.filiacao_set.get(Q(
data__lte=legislatura.data_fim, data__lte=legislatura.data_fim,
data_desfiliacao__gte=legislatura.data_fim) | Q( data_desfiliacao__gte=legislatura.data_fim) | Q(
@ -574,7 +593,8 @@ class ParlamentarCrud(Crud):
# Caso encontre UMA filiação nessas condições # Caso encontre UMA filiação nessas condições
else: else:
self.logger.debug("user=" + username + ". Filiação encontrada com sucesso.") self.logger.debug("user=" + username +
". Filiação encontrada com sucesso.")
row[1] = (filiacao.partido.sigla, None, None) row[1] = (filiacao.partido.sigla, None, None)
return context return context
@ -606,13 +626,16 @@ class ParlamentarMateriasView(FormView):
parlamentar_pk = kwargs['pk'] parlamentar_pk = kwargs['pk']
username = request.user.username username = request.user.username
try: try:
self.logger.debug("user=" + username + ". Tentando obter Autor (object_id={}).".format(parlamentar_pk)) self.logger.debug(
"user=" + username + ". Tentando obter Autor (object_id={}).".format(parlamentar_pk))
autor = Autor.objects.get( autor = Autor.objects.get(
content_type=ContentType.objects.get_for_model(Parlamentar), content_type=ContentType.objects.get_for_model(Parlamentar),
object_id=parlamentar_pk) object_id=parlamentar_pk)
except ObjectDoesNotExist: except ObjectDoesNotExist:
mensagem = _('Este Parlamentar (pk={}) não é Autor de matéria.'.format(parlamentar_pk)) mensagem = _(
self.logger.error("user=" + username + ". Este Parlamentar (pk={}) não é Autor de matéria.".format(parlamentar_pk)) '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) messages.add_message(request, messages.ERROR, mensagem)
return HttpResponseRedirect( return HttpResponseRedirect(
reverse( reverse(
@ -700,7 +723,8 @@ class MesaDiretoraView(FormView):
sessao_atual = sessoes.filter(data_inicio__year__lte=year).exclude( sessao_atual = sessoes.filter(data_inicio__year__lte=year).exclude(
data_inicio__gt=timezone.now()).order_by('-data_inicio').first() data_inicio__gt=timezone.now()).order_by('-data_inicio').first()
mesa = sessao_atual.composicaomesa_set.all().order_by('cargo_id') if sessao_atual else [] mesa = sessao_atual.composicaomesa_set.all().order_by(
'cargo_id') if sessao_atual else []
cargos_ocupados = [m.cargo for m in mesa] cargos_ocupados = [m.cargo for m in mesa]
cargos = CargoMesa.objects.all() cargos = CargoMesa.objects.all()
@ -756,7 +780,8 @@ def altera_field_mesa(request):
else: else:
year = timezone.now().year year = timezone.now().year
try: try:
logger.debug("user=" + username + ". Tentando obter id de sessoes com data_inicio.ano={}.".format(year)) logger.debug(
"user=" + username + ". Tentando obter id de sessoes com data_inicio.ano={}.".format(year))
sessao_selecionada = sessoes.get(data_inicio__year=year).id sessao_selecionada = sessoes.get(data_inicio__year=year).id
except ObjectDoesNotExist: except ObjectDoesNotExist:
logger.error("user=" + username + ". Id de sessoes com data_inicio.ano={} não encontrado. " logger.error("user=" + username + ". Id de sessoes com data_inicio.ano={} não encontrado. "
@ -809,24 +834,29 @@ def insere_parlamentar_composicao(request):
composicao = ComposicaoMesa() composicao = ComposicaoMesa()
try: try:
logger.debug("user=" + username + ". Tentando obter SessaoLegislativa com id={}.".format(request.POST['sessao'])) logger.debug(
"user=" + username + ". Tentando obter SessaoLegislativa com id={}.".format(request.POST['sessao']))
composicao.sessao_legislativa = SessaoLegislativa.objects.get( composicao.sessao_legislativa = SessaoLegislativa.objects.get(
id=int(request.POST['sessao'])) id=int(request.POST['sessao']))
except MultiValueDictKeyError: except MultiValueDictKeyError:
logger.error("user=" + username + ". 'MultiValueDictKeyError', nenhuma sessão foi inserida!") logger.error(
"user=" + username + ". 'MultiValueDictKeyError', nenhuma sessão foi inserida!")
return JsonResponse({'msg': ('Nenhuma sessão foi inserida!', 0)}) return JsonResponse({'msg': ('Nenhuma sessão foi inserida!', 0)})
try: try:
logger.debug("user=" + username + ". Tentando obter Parlamentar com id={}.".format(request.POST['parlamentar'])) logger.debug(
"user=" + username + ". Tentando obter Parlamentar com id={}.".format(request.POST['parlamentar']))
composicao.parlamentar = Parlamentar.objects.get( composicao.parlamentar = Parlamentar.objects.get(
id=int(request.POST['parlamentar'])) id=int(request.POST['parlamentar']))
except MultiValueDictKeyError: except MultiValueDictKeyError:
logger.error("user=" + username + ". 'MultiValueDictKeyError', nenhum parlamentar foi inserido!") logger.error(
"user=" + username + ". 'MultiValueDictKeyError', nenhum parlamentar foi inserido!")
return JsonResponse({ return JsonResponse({
'msg': ('Nenhum parlamentar foi inserido!', 0)}) 'msg': ('Nenhum parlamentar foi inserido!', 0)})
try: try:
logger.info("user=" + username + ". Tentando obter CargoMesa com id={}.".format(request.POST['cargo'])) logger.info("user=" + username +
". Tentando obter CargoMesa com id={}.".format(request.POST['cargo']))
composicao.cargo = CargoMesa.objects.get( composicao.cargo = CargoMesa.objects.get(
id=int(request.POST['cargo'])) id=int(request.POST['cargo']))
parlamentar_ja_inserido = ComposicaoMesa.objects.filter( parlamentar_ja_inserido = ComposicaoMesa.objects.filter(
@ -839,14 +869,16 @@ def insere_parlamentar_composicao(request):
composicao.save() composicao.save()
except MultiValueDictKeyError: except MultiValueDictKeyError:
logger.error("user=" + username + ". 'MultiValueDictKeyError', nenhum cargo foi inserido!") logger.error("user=" + username +
". 'MultiValueDictKeyError', nenhum cargo foi inserido!")
return JsonResponse({'msg': ('Nenhum cargo foi inserido!', 0)}) return JsonResponse({'msg': ('Nenhum cargo foi inserido!', 0)})
logger.info("user=" + username + ". Parlamentar inserido com sucesso!") logger.info("user=" + username + ". Parlamentar inserido com sucesso!")
return JsonResponse({'msg': ('Parlamentar inserido com sucesso!', 1)}) return JsonResponse({'msg': ('Parlamentar inserido com sucesso!', 1)})
else: else:
logger.error("user=" + username + " não tem permissão para esta operação!") logger.error("user=" + username +
" não tem permissão para esta operação!")
return JsonResponse( return JsonResponse(
{'msg': ('Você não tem permissão para esta operação!', 0)}) {'msg': ('Você não tem permissão para esta operação!', 0)})
@ -864,7 +896,8 @@ def remove_parlamentar_composicao(request):
if 'composicao_mesa' in request.POST: if 'composicao_mesa' in request.POST:
try: try:
logger.debug("user=" + username + ". Tentando obter ComposicaoMesa com id={}.".format(request.POST['composicao_mesa'])) logger.debug("user=" + username + ". Tentando obter ComposicaoMesa com id={}.".format(
request.POST['composicao_mesa']))
composicao = ComposicaoMesa.objects.get( composicao = ComposicaoMesa.objects.get(
id=request.POST['composicao_mesa']) id=request.POST['composicao_mesa'])
except ObjectDoesNotExist: except ObjectDoesNotExist:
@ -876,12 +909,14 @@ def remove_parlamentar_composicao(request):
composicao.delete() composicao.delete()
logger.info("user=" + username + ". ComposicaoMesa com id={} excluido com sucesso!".format(request.POST['composicao_mesa'])) logger.info("user=" + username + ". ComposicaoMesa com id={} excluido com sucesso!".format(
request.POST['composicao_mesa']))
return JsonResponse( return JsonResponse(
{'msg': ( {'msg': (
'Parlamentar excluido com sucesso!', 1)}) 'Parlamentar excluido com sucesso!', 1)})
else: else:
logger.info("user=" + username + ". Nenhum parlamentar escolhido para ser excluído.") logger.info("user=" + username +
". Nenhum parlamentar escolhido para ser excluído.")
return JsonResponse( return JsonResponse(
{'msg': ( {'msg': (
'Selecione algum parlamentar para ser excluido!', 0)}) 'Selecione algum parlamentar para ser excluido!', 0)})
@ -900,8 +935,8 @@ def partido_parlamentar_sessao_legislativa(sessao, parlamentar):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
try: try:
logger.debug("Tentando obter filiação do parlamentar com (data<={} e data_desfiliacao>={}) " logger.debug("Tentando obter filiação do parlamentar com (data<={} e data_desfiliacao>={}) "
"ou (data<={} e data_desfiliacao=Null))." "ou (data<={} e data_desfiliacao=Null))."
.format(sessao.data_fim, sessao.data_fim, sessao.data_fim)) .format(sessao.data_fim, sessao.data_fim, sessao.data_fim))
logger.info("Tentando obter filiação correspondente.") logger.info("Tentando obter filiação correspondente.")
filiacao = parlamentar.filiacao_set.get(Q( filiacao = parlamentar.filiacao_set.get(Q(
@ -957,7 +992,8 @@ def altera_field_mesa_public_view(request):
else: else:
try: try:
year = timezone.now().year year = timezone.now().year
logger.info("user=" + username + ". Tentando obter sessões com data_inicio.ano = {}.".format(year)) logger.info("user=" + username +
". Tentando obter sessões com data_inicio.ano = {}.".format(year))
sessao_selecionada = sessoes.get(data_inicio__year=year).id sessao_selecionada = sessoes.get(data_inicio__year=year).id
except ObjectDoesNotExist: except ObjectDoesNotExist:
logger.error("user=" + username + ". Sessões não encontradas com com data_inicio.ano = {}. " logger.error("user=" + username + ". Sessões não encontradas com com data_inicio.ano = {}. "

155
sapl/protocoloadm/forms.py

@ -1,9 +1,9 @@
import logging import logging
from crispy_forms.bootstrap import InlineRadios from crispy_forms.bootstrap import InlineRadios, Alert
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Div
from django import forms from django import forms
from django.core.exceptions import (MultipleObjectsReturned, from django.core.exceptions import (MultipleObjectsReturned,
ObjectDoesNotExist, ValidationError) ObjectDoesNotExist, ValidationError)
@ -18,11 +18,12 @@ from sapl.base.models import Autor, TipoAutor
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row
from sapl.materia.models import (MateriaLegislativa, TipoMateriaLegislativa, from sapl.materia.models import (MateriaLegislativa, TipoMateriaLegislativa,
UnidadeTramitacao) UnidadeTramitacao)
from sapl.protocoloadm.models import Protocolo
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, AnoNumeroOrderingFilter, from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, AnoNumeroOrderingFilter,
RangeWidgetOverride, autor_label, autor_modal, RangeWidgetOverride, autor_label, autor_modal,
choice_anos_com_protocolo, choice_force_optional, choice_anos_com_protocolo, choice_force_optional,
choice_anos_com_documentoadministrativo, choice_anos_com_documentoadministrativo,
FilterOverridesMetaMixin) FilterOverridesMetaMixin, choice_anos_com_materias)
from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo, from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo,
DocumentoAdministrativo, DocumentoAdministrativo,
@ -344,6 +345,12 @@ class ProtocoloDocumentForm(ModelForm):
numero = forms.IntegerField( numero = forms.IntegerField(
required=False, label=_('Número de Protocolo (opcional)')) required=False, label=_('Número de Protocolo (opcional)'))
data_hora_manual = forms.ChoiceField(
label=_('Informar data e hora manualmente?'),
widget=forms.RadioSelect(),
choices=YES_NO_CHOICES,
initial=False)
class Meta: class Meta:
model = Protocolo model = Protocolo
fields = ['tipo_protocolo', fields = ['tipo_protocolo',
@ -352,7 +359,9 @@ class ProtocoloDocumentForm(ModelForm):
'assunto', 'assunto',
'interessado', 'interessado',
'observacao', 'observacao',
'numero' 'numero',
'data',
'hora',
] ]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -360,30 +369,56 @@ class ProtocoloDocumentForm(ModelForm):
row1 = to_row( row1 = to_row(
[(InlineRadios('tipo_protocolo'), 12)]) [(InlineRadios('tipo_protocolo'), 12)])
row2 = to_row( row2 = to_row(
[('tipo_documento', 6), [('tipo_documento', 5),
('numero_paginas', 6)]) ('numero_paginas', 2),
row3 = to_row( (Div(), 1),
[('assunto', 12)]) (InlineRadios('data_hora_manual'), 4),
])
row3 = to_row([
(Div(), 2),
(Alert(
"""
Usuário: <strong>{}</strong> - {}<br>
IP: <strong>{}</strong> - {}<br>
""".format(
kwargs['initial']['user_data_hora_manual'],
Protocolo._meta.get_field(
'user_data_hora_manual').help_text,
kwargs['initial']['ip_data_hora_manual'],
Protocolo._meta.get_field(
'ip_data_hora_manual').help_text,
),
dismiss=False,
css_class='alert-info'), 6),
('data', 2),
('hora', 2),
])
row4 = to_row( row4 = to_row(
[('interessado', 12)]) [('assunto', 12)])
row5 = to_row( row5 = to_row(
[('observacao', 12)]) [('interessado', 12)])
row6 = to_row( row6 = to_row(
[('observacao', 12)])
row7 = to_row(
[('numero', 12)]) [('numero', 12)])
self.helper = FormHelper() self.helper = FormHelper()
self.helper.layout = Layout( self.helper.layout = Layout(
Fieldset(_('Identificação de Documento'), Fieldset(_('Identificação de Documento'),
row1, row1,
row2, row2),
Fieldset(_('Protocolo com data e hora informados manualmente'),
row3, row3,
row4, css_id='protocolo_data_hora_manual',
row5, css_class='hidden'),
HTML("&nbsp;"), row4,
), row5,
HTML("&nbsp;"),
Fieldset(_('Número do Protocolo (Apenas se quiser que a numeração comece ' Fieldset(_('Número do Protocolo (Apenas se quiser que a numeração comece '
'a partir do número a ser informado)'), 'a partir do número a ser informado)'),
row6, row7,
HTML("&nbsp;"), HTML("&nbsp;"),
form_actions(label=_('Protocolar Documento')) form_actions(label=_('Protocolar Documento'))
) )
@ -419,10 +454,11 @@ class ProtocoloMateriaForm(ModelForm):
ano_materia = forms.CharField( ano_materia = forms.CharField(
label=_('Ano matéria'), required=False) label=_('Ano matéria'), required=False)
vincular_materia = forms.ChoiceField(label=_('Vincular a matéria existente?'), vincular_materia = forms.ChoiceField(
widget=forms.RadioSelect(), label=_('Vincular a matéria existente?'),
choices=YES_NO_CHOICES, widget=forms.RadioSelect(),
initial=False) choices=YES_NO_CHOICES,
initial=False)
numero_paginas = forms.CharField(label=_('Núm. Páginas'), required=True) numero_paginas = forms.CharField(label=_('Núm. Páginas'), required=True)
@ -435,6 +471,12 @@ class ProtocoloMateriaForm(ModelForm):
numero = forms.IntegerField( numero = forms.IntegerField(
required=False, label=_('Número de Protocolo (opcional)')) required=False, label=_('Número de Protocolo (opcional)'))
data_hora_manual = forms.ChoiceField(
label=_('Informar data e hora manualmente?'),
widget=forms.RadioSelect(),
choices=YES_NO_CHOICES,
initial=False)
class Meta: class Meta:
model = Protocolo model = Protocolo
fields = ['tipo_materia', fields = ['tipo_materia',
@ -446,7 +488,9 @@ class ProtocoloMateriaForm(ModelForm):
'numero_materia', 'numero_materia',
'ano_materia', 'ano_materia',
'vincular_materia', 'vincular_materia',
'numero' 'numero',
'data',
'hora',
] ]
def clean_autor(self): def clean_autor(self):
@ -506,28 +550,55 @@ class ProtocoloMateriaForm(ModelForm):
('tipo_autor', 3), ('tipo_autor', 3),
('autor', 3)]) ('autor', 3)])
row2 = to_row( row2 = to_row(
[(InlineRadios('vincular_materia'), 4), [(InlineRadios('vincular_materia'), 3),
('numero_materia', 4), ('numero_materia', 2),
('ano_materia', 4), ]) ('ano_materia', 2),
row3 = to_row( (Div(), 1),
[('assunto_ementa', 12)]) (InlineRadios('data_hora_manual'), 4),
])
row3 = to_row([
(Div(), 2),
(Alert(
"""
Usuário: <strong>{}</strong> - {}<br>
IP: <strong>{}</strong> - {}<br>
""".format(
kwargs['initial']['user_data_hora_manual'],
Protocolo._meta.get_field(
'user_data_hora_manual').help_text,
kwargs['initial']['ip_data_hora_manual'],
Protocolo._meta.get_field(
'ip_data_hora_manual').help_text,
),
dismiss=False,
css_class='alert-info'), 6),
('data', 2),
('hora', 2),
])
row4 = to_row( row4 = to_row(
[('observacao', 12)]) [('assunto_ementa', 12)])
row5 = to_row( row5 = to_row(
[('observacao', 12)])
row6 = to_row(
[('numero', 12)]) [('numero', 12)])
self.helper = FormHelper() self.helper = FormHelper()
self.helper.layout = Layout( self.helper.layout = Layout(
Fieldset(_('Identificação da Matéria'), Fieldset(_('Identificação da Matéria'),
row1, row1,
row2, row2),
Fieldset(_('Protocolo com data e hora informados manualmente'),
row3, row3,
row4, css_id='protocolo_data_hora_manual',
HTML("&nbsp;"), css_class='hidden'),
), row4,
row5,
HTML("&nbsp;"),
Fieldset(_('Número do Protocolo (Apenas se quiser que a numeração comece' Fieldset(_('Número do Protocolo (Apenas se quiser que a numeração comece'
' a partir do número a ser informado)'), ' a partir do número a ser informado)'),
row5, row6,
HTML("&nbsp;"), HTML("&nbsp;"),
form_actions(label=_('Protocolar Matéria'))) form_actions(label=_('Protocolar Matéria')))
) )
@ -855,15 +926,15 @@ class DesvincularDocumentoForm(ModelForm):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
numero = forms.CharField(required=True, numero = forms.CharField(
label=DocumentoAdministrativo._meta. required=True,
get_field('numero').verbose_name label=DocumentoAdministrativo._meta.get_field('numero').verbose_name)
)
ano = forms.ChoiceField(required=True, ano = forms.ChoiceField(
label=DocumentoAdministrativo._meta. required=True,
get_field('ano').verbose_name, label=DocumentoAdministrativo._meta.get_field('ano').verbose_name,
choices=RANGE_ANOS, choices=choice_anos_com_documentoadministrativo,
widget=forms.Select(attrs={'class': 'selector'})) widget=forms.Select(attrs={'class': 'selector'}))
def clean(self): def clean(self):
super(DesvincularDocumentoForm, self).clean() super(DesvincularDocumentoForm, self).clean()
@ -929,7 +1000,7 @@ class DesvincularMateriaForm(forms.Form):
label=_('Número da Matéria')) label=_('Número da Matéria'))
ano = forms.ChoiceField(required=True, ano = forms.ChoiceField(required=True,
label=_('Ano da Matéria'), label=_('Ano da Matéria'),
choices=RANGE_ANOS, choices=choice_anos_com_materias,
widget=forms.Select(attrs={'class': 'selector'})) widget=forms.Select(attrs={'class': 'selector'}))
tipo = forms.ModelChoiceField(label=_('Tipo de Matéria'), tipo = forms.ModelChoiceField(label=_('Tipo de Matéria'),
required=True, required=True,

35
sapl/protocoloadm/migrations/0014_auto_20190110_1300.py

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.8 on 2019-01-10 15:00
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0013_auto_20190106_1336'),
]
operations = [
migrations.AddField(
model_name='protocolo',
name='ip_data_hora_manual',
field=models.CharField(blank=True, help_text='Endereço IP da estação de trabalho do usuário que está realizando Protocolo e informando data e hora manualmente.', max_length=15, verbose_name='IP'),
),
migrations.AddField(
model_name='protocolo',
name='user_data_hora_manual',
field=models.CharField(blank=True, help_text='Usuário que está realizando Protocolo e informando data e hora manualmente.', max_length=20, verbose_name='IP'),
),
migrations.AlterField(
model_name='protocolo',
name='data',
field=models.DateField(blank=True, help_text='Informado manualmente', null=True, verbose_name='Data do Protocolo'),
),
migrations.AlterField(
model_name='protocolo',
name='hora',
field=models.TimeField(blank=True, help_text='Informado manualmente', null=True, verbose_name='Hora do Protocolo'),
),
]

21
sapl/protocoloadm/migrations/0015_protocolo_timestamp_data_hora_manual.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.8 on 2019-01-10 15:43
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0014_auto_20190110_1300'),
]
operations = [
migrations.AddField(
model_name='protocolo',
name='timestamp_data_hora_manual',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]

21
sapl/protocoloadm/migrations/0016_auto_20190110_1345.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.8 on 2019-01-10 15:45
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0015_protocolo_timestamp_data_hora_manual'),
]
operations = [
migrations.AlterField(
model_name='protocolo',
name='timestamp',
field=models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True),
),
]

24
sapl/protocoloadm/models.py

@ -57,13 +57,29 @@ class Protocolo(models.Model):
choices=RANGE_ANOS, choices=RANGE_ANOS,
verbose_name=_('Ano do Protocolo')) verbose_name=_('Ano do Protocolo'))
# FIXME: https://github.com/interlegis/sapl/issues/2337 data = models.DateField(null=True, blank=True,
data = models.DateField(null=True, blank=True) verbose_name=_('Data do Protocolo'),
hora = models.TimeField(null=True, blank=True) help_text=_('Informado manualmente'))
hora = models.TimeField(null=True, blank=True,
verbose_name=_('Hora do Protocolo'),
help_text=_('Informado manualmente'))
timestamp_data_hora_manual = models.DateTimeField(default=timezone.now)
user_data_hora_manual = models.CharField(
max_length=20, blank=True,
verbose_name=_('IP'),
help_text=_('Usuário que está realizando Protocolo e informando '
'data e hora manualmente.'))
ip_data_hora_manual = models.CharField(
max_length=15, blank=True,
verbose_name=_('IP'),
help_text=_('Endereço IP da estação de trabalho '
'do usuário que está realizando Protocolo e informando '
'data e hora manualmente.'))
# Não foi utilizado auto_now_add=True em timestamp porque # Não foi utilizado auto_now_add=True em timestamp porque
# ele usa datetime.now que não é timezone aware. # ele usa datetime.now que não é timezone aware.
timestamp = models.DateTimeField(default=timezone.now) timestamp = models.DateTimeField(
default=timezone.now, null=True, blank=True)
tipo_protocolo = models.PositiveIntegerField( tipo_protocolo = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Tipo de Protocolo')) blank=True, null=True, verbose_name=_('Tipo de Protocolo'))
tipo_processo = models.PositiveIntegerField() tipo_processo = models.PositiveIntegerField()

23
sapl/protocoloadm/tests/test_protocoloadm.py

@ -1,4 +1,4 @@
from datetime import date, timedelta from datetime import date, timedelta, datetime
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils import timezone from django.utils import timezone
@ -392,29 +392,42 @@ def test_documento_administrativo_protocolo_inexistente():
def test_protocolo_documento_form_invalido(): def test_protocolo_documento_form_invalido():
form = ProtocoloDocumentForm(data={}) form = ProtocoloDocumentForm(
data={},
initial={
'user_data_hora_manual': '',
'ip_data_hora_manual': '',
'data': timezone.localdate(timezone.now()),
'hora': timezone.localtime(timezone.now())})
assert not form.is_valid() assert not form.is_valid()
errors = form.errors errors = form.errors
assert errors['data_hora_manual'] == [_('Este campo é obrigatório.')]
assert errors['tipo_protocolo'] == [_('Este campo é obrigatório.')] assert errors['tipo_protocolo'] == [_('Este campo é obrigatório.')]
assert errors['interessado'] == [_('Este campo é obrigatório.')] assert errors['interessado'] == [_('Este campo é obrigatório.')]
assert errors['tipo_documento'] == [_('Este campo é obrigatório.')] assert errors['tipo_documento'] == [_('Este campo é obrigatório.')]
assert errors['numero_paginas'] == [_('Este campo é obrigatório.')] assert errors['numero_paginas'] == [_('Este campo é obrigatório.')]
assert errors['assunto'] == [_('Este campo é obrigatório.')] assert errors['assunto'] == [_('Este campo é obrigatório.')]
assert len(errors) == 5 assert len(errors) == 6
def test_protocolo_materia_invalido(): def test_protocolo_materia_invalido():
form = ProtocoloMateriaForm(data={}) form = ProtocoloMateriaForm(data={},
initial={
'user_data_hora_manual': '',
'ip_data_hora_manual': '',
'data': timezone.localdate(timezone.now()),
'hora': timezone.localtime(timezone.now())})
assert not form.is_valid() assert not form.is_valid()
errors = form.errors errors = form.errors
assert errors['data_hora_manual'] == [_('Este campo é obrigatório.')]
assert errors['assunto_ementa'] == [_('Este campo é obrigatório.')] assert errors['assunto_ementa'] == [_('Este campo é obrigatório.')]
assert errors['tipo_autor'] == [_('Este campo é obrigatório.')] assert errors['tipo_autor'] == [_('Este campo é obrigatório.')]
assert errors['tipo_materia'] == [_('Este campo é obrigatório.')] assert errors['tipo_materia'] == [_('Este campo é obrigatório.')]
@ -422,4 +435,4 @@ def test_protocolo_materia_invalido():
assert errors['autor'] == [_('Este campo é obrigatório.')] assert errors['autor'] == [_('Este campo é obrigatório.')]
assert errors['vincular_materia'] == [_('Este campo é obrigatório.')] assert errors['vincular_materia'] == [_('Este campo é obrigatório.')]
assert len(errors) == 6 assert len(errors) == 7

39
sapl/protocoloadm/views.py

@ -492,6 +492,15 @@ class ProtocoloDocumentoView(PermissionRequiredMixin,
return reverse('sapl.protocoloadm:protocolo_mostrar', return reverse('sapl.protocoloadm:protocolo_mostrar',
kwargs={'pk': self.object.id}) kwargs={'pk': self.object.id})
def get_initial(self):
initial = super().get_initial()
initial['user_data_hora_manual'] = self.request.user.username
initial['ip_data_hora_manual'] = get_client_ip(self.request)
initial['data'] = timezone.localdate(timezone.now())
initial['hora'] = timezone.localtime(timezone.now())
return initial
def form_valid(self, form): def form_valid(self, form):
protocolo = form.save(commit=False) protocolo = form.save(commit=False)
username = self.request.user.username username = self.request.user.username
@ -538,6 +547,17 @@ class ProtocoloDocumentoView(PermissionRequiredMixin,
return self.render_to_response(self.get_context_data()) return self.render_to_response(self.get_context_data())
protocolo.ano = timezone.now().year protocolo.ano = timezone.now().year
protocolo.assunto_ementa = self.request.POST['assunto'] protocolo.assunto_ementa = self.request.POST['assunto']
if form.cleaned_data['data_hora_manual'] == 'True':
protocolo.timestamp = None
protocolo.user_data_hora_manual = username
protocolo.ip_data_hora_manual = get_client_ip(self.request)
else:
protocolo.data = None
protocolo.hora = None
protocolo.user_data_hora_manual = ''
protocolo.ip_data_hora_manual = ''
protocolo.save() protocolo.save()
self.object = protocolo self.object = protocolo
return redirect(self.get_success_url()) return redirect(self.get_success_url())
@ -659,6 +679,15 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
return reverse('sapl.protocoloadm:materia_continuar', kwargs={ return reverse('sapl.protocoloadm:materia_continuar', kwargs={
'pk': protocolo.pk}) 'pk': protocolo.pk})
def get_initial(self):
initial = super().get_initial()
initial['user_data_hora_manual'] = self.request.user.username
initial['ip_data_hora_manual'] = get_client_ip(self.request)
initial['data'] = timezone.localdate(timezone.now())
initial['hora'] = timezone.localtime(timezone.now())
return initial
def form_valid(self, form): def form_valid(self, form):
protocolo = form.save(commit=False) protocolo = form.save(commit=False)
username = self.request.user.username username = self.request.user.username
@ -719,6 +748,16 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
protocolo.observacao = self.request.POST['observacao'] protocolo.observacao = self.request.POST['observacao']
protocolo.assunto_ementa = self.request.POST['assunto_ementa'] protocolo.assunto_ementa = self.request.POST['assunto_ementa']
if form.cleaned_data['data_hora_manual'] == 'True':
protocolo.timestamp = None
protocolo.user_data_hora_manual = username
protocolo.ip_data_hora_manual = get_client_ip(self.request)
else:
protocolo.data = None
protocolo.hora = None
protocolo.user_data_hora_manual = ''
protocolo.ip_data_hora_manual = ''
protocolo.save() protocolo.save()
data = form.cleaned_data data = form.cleaned_data
if data['vincular_materia'] == 'True': if data['vincular_materia'] == 'True':

33
sapl/relatorios/views.py

@ -1,7 +1,7 @@
from datetime import datetime as dt
import html import html
import re
import logging import logging
from datetime import datetime as dt import re
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
@ -581,7 +581,6 @@ def get_sessao_plenaria(sessao, casa):
if dic_expedientes: if dic_expedientes:
lst_expedientes.append(dic_expedientes) lst_expedientes.append(dic_expedientes)
# Lista das matérias do Expediente, incluindo o resultado das votacoes # Lista das matérias do Expediente, incluindo o resultado das votacoes
lst_expediente_materia = [] lst_expediente_materia = []
for expediente_materia in ExpedienteMateria.objects.filter( for expediente_materia in ExpedienteMateria.objects.filter(
@ -612,7 +611,8 @@ def get_sessao_plenaria(sessao, casa):
dic_expediente_materia["nom_autor"] = '' dic_expediente_materia["nom_autor"] = ''
autoria = materia.autoria_set.all() autoria = materia.autoria_set.all()
dic_expediente_materia['num_autores'] = 'Autores' if len(autoria) > 1 else 'Autor' dic_expediente_materia['num_autores'] = 'Autores' if len(
autoria) > 1 else 'Autor'
if autoria: if autoria:
for a in autoria: for a in autoria:
if a.autor.nome: if a.autor.nome:
@ -762,7 +762,6 @@ def get_sessao_plenaria(sessao, casa):
lst_ocorrencias.append(o) lst_ocorrencias.append(o)
return (inf_basicas_dic, return (inf_basicas_dic,
lst_mesa, lst_mesa,
lst_presenca_sessao, lst_presenca_sessao,
@ -810,10 +809,12 @@ def relatorio_sessao_plenaria(request, pk):
imagem = get_imagem(casa) imagem = get_imagem(casa)
try: try:
logger.debug("user=" + username + ". Tentando obter SessaoPlenaria com id={}.".format(pk)) logger.debug("user=" + username +
". Tentando obter SessaoPlenaria com id={}.".format(pk))
sessao = SessaoPlenaria.objects.get(id=pk) sessao = SessaoPlenaria.objects.get(id=pk)
except ObjectDoesNotExist as e: except ObjectDoesNotExist as e:
logger.error("user=" + username + ". Essa SessaoPlenaria não existe (pk={}). ".format(pk) + str(e)) logger.error("user=" + username +
". Essa SessaoPlenaria não existe (pk={}). ".format(pk) + str(e))
raise Http404('Essa página não existe') raise Http404('Essa página não existe')
(inf_basicas_dic, (inf_basicas_dic,
@ -828,11 +829,10 @@ def relatorio_sessao_plenaria(request, pk):
lst_oradores, lst_oradores,
lst_ocorrencias) = get_sessao_plenaria(sessao, casa) lst_ocorrencias) = get_sessao_plenaria(sessao, casa)
for idx in range(len(lst_expedientes)): for idx in range(len(lst_expedientes)):
txt_expedientes = lst_expedientes[idx]['txt_expediente'] txt_expedientes = lst_expedientes[idx]['txt_expediente']
txt_expedientes = TrocaTag(txt_expedientes, '<table', 'table>', 6, 6, txt_expedientes = TrocaTag(txt_expedientes, '<table', 'table>', 6, 6,
'expedientes', '</para><blockTable style = "', 'blockTable><para>') 'expedientes', '</para><blockTable style = "', 'blockTable><para>')
lst_expedientes[idx]['txt_expediente'] = txt_expedientes lst_expedientes[idx]['txt_expediente'] = txt_expedientes
pdf = pdf_sessao_plenaria_gerar.principal( pdf = pdf_sessao_plenaria_gerar.principal(
@ -868,7 +868,7 @@ def get_protocolos(prots):
ts.strftime("%H:%m") ts.strftime("%H:%m")
else: else:
dic['data'] = protocolo.data.strftime("%d/%m/%Y") + ' - <b>Horário:</b>' \ dic['data'] = protocolo.data.strftime("%d/%m/%Y") + ' - <b>Horário:</b>' \
+ protocolo.hora.strftime("%H:%m") + protocolo.hora.strftime("%H:%m")
dic['txt_assunto'] = protocolo.assunto_ementa dic['txt_assunto'] = protocolo.assunto_ementa
@ -979,8 +979,8 @@ def get_etiqueta_protocolos(prots):
dic['titulo'] = str(p.numero) + '/' + str(p.ano) dic['titulo'] = str(p.numero) + '/' + str(p.ano)
tz_hora = timezone.localtime(p.timestamp)
if p.timestamp: if p.timestamp:
tz_hora = timezone.localtime(p.timestamp)
dic['data'] = '<b>Data: </b>' + tz_hora.strftime( dic['data'] = '<b>Data: </b>' + tz_hora.strftime(
"%d/%m/%Y") + ' - <b>Horário: </b>' + tz_hora.strftime("%H:%M") "%d/%m/%Y") + ' - <b>Horário: </b>' + tz_hora.strftime("%H:%M")
else: else:
@ -1072,7 +1072,8 @@ def get_pauta_sessao(sessao, casa):
id=expediente_materia.materia.id).first() id=expediente_materia.materia.id).first()
dic_expediente_materia = {} dic_expediente_materia = {}
dic_expediente_materia["tipo_materia"] = materia.tipo.sigla + ' - ' + materia.tipo.descricao dic_expediente_materia["tipo_materia"] = materia.tipo.sigla + \
' - ' + materia.tipo.descricao
dic_expediente_materia["num_ordem"] = str( dic_expediente_materia["num_ordem"] = str(
expediente_materia.numero_ordem) expediente_materia.numero_ordem)
dic_expediente_materia["id_materia"] = str( dic_expediente_materia["id_materia"] = str(
@ -1090,7 +1091,8 @@ def get_pauta_sessao(sessao, casa):
dic_expediente_materia["nom_autor"] = '' dic_expediente_materia["nom_autor"] = ''
autoria = materia.autoria_set.all() autoria = materia.autoria_set.all()
dic_expediente_materia['num_autores'] = 'Autores' if len(autoria) > 1 else 'Autor' dic_expediente_materia['num_autores'] = 'Autores' if len(
autoria) > 1 else 'Autor'
if autoria: if autoria:
for a in autoria: for a in autoria:
if a.autor.nome: if a.autor.nome:
@ -1112,7 +1114,8 @@ def get_pauta_sessao(sessao, casa):
materia = MateriaLegislativa.objects.filter( materia = MateriaLegislativa.objects.filter(
id=votacao.materia.id).first() id=votacao.materia.id).first()
dic_votacao = {} dic_votacao = {}
dic_votacao["tipo_materia"] = materia.tipo.sigla + ' - ' + materia.tipo.descricao dic_votacao["tipo_materia"] = materia.tipo.sigla + \
' - ' + materia.tipo.descricao
dic_votacao["num_ordem"] = votacao.numero_ordem dic_votacao["num_ordem"] = votacao.numero_ordem
dic_votacao["id_materia"] = str( dic_votacao["id_materia"] = str(
materia.numero) + "/" + str(materia.ano) materia.numero) + "/" + str(materia.ano)
@ -1124,7 +1127,7 @@ def get_pauta_sessao(sessao, casa):
numeracao = Numeracao.objects.filter(materia=votacao.materia).first() numeracao = Numeracao.objects.filter(materia=votacao.materia).first()
if numeracao: if numeracao:
dic_votacao["des_numeracao"] = str( dic_votacao["des_numeracao"] = str(
numeracao.numero_materia) + '/' + str(numeracao.ano_materia) numeracao.numero_materia) + '/' + str(numeracao.ano_materia)
turno, tramitacao = get_turno(materia) turno, tramitacao = get_turno(materia)
dic_votacao["des_turno"] = turno dic_votacao["des_turno"] = turno

8
sapl/sessao/forms.py

@ -3,7 +3,6 @@ from datetime import datetime
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button, Fieldset, Layout from crispy_forms.layout import HTML, Button, Fieldset, Layout
from django import forms from django import forms
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import transaction from django.db import transaction
@ -12,22 +11,21 @@ from django.forms import ModelForm
from django.forms.widgets import CheckboxSelectMultiple from django.forms.widgets import CheckboxSelectMultiple
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import django_filters import django_filters
from floppyforms import widgets
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.crispy_layout_mixin import form_actions, to_row, SaplFormLayout from sapl.crispy_layout_mixin import form_actions, to_row, SaplFormLayout
from sapl.materia.forms import MateriaLegislativaFilterSet from sapl.materia.forms import MateriaLegislativaFilterSet
from sapl.materia.models import (MateriaLegislativa, StatusTramitacao, from sapl.materia.models import (MateriaLegislativa, StatusTramitacao,
TipoMateriaLegislativa) TipoMateriaLegislativa)
from sapl.parlamentares.models import Parlamentar, Legislatura, Mandato from sapl.parlamentares.models import Parlamentar, Mandato
from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES, from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES,
MateriaPesquisaOrderingFilter, autor_label, MateriaPesquisaOrderingFilter, autor_label,
autor_modal, timezone, choice_anos_com_sessaoplenaria) autor_modal, timezone, choice_anos_com_sessaoplenaria)
from .models import (Bancada, Bloco, ExpedienteMateria, JustificativaAusencia, from .models import (Bancada, Bloco, ExpedienteMateria, JustificativaAusencia,
Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, TipoJustificativa, TipoResultadoVotacao, SessaoPlenariaPresenca, TipoResultadoVotacao,
OcorrenciaSessao, RegistroVotacao, RetiradaPauta, TipoRetiradaPauta) OcorrenciaSessao, RetiradaPauta, TipoRetiradaPauta)
MES_CHOICES = RANGE_MESES MES_CHOICES = RANGE_MESES

47
sapl/settings.py

@ -22,9 +22,6 @@ from dj_database_url import parse as db_url
from easy_thumbnails.conf import Settings as thumbnail_settings from easy_thumbnails.conf import Settings as thumbnail_settings
from unipath import Path from unipath import Path
from .temp_suppress_crispy_form_warnings import \
SUPRESS_CRISPY_FORM_WARNINGS_LOGGING
host = socket.gethostbyname_ex(socket.gethostname())[0] host = socket.gethostbyname_ex(socket.gethostname())[0]
@ -78,20 +75,24 @@ INSTALLED_APPS = (
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
# more
'django_extensions', 'django_extensions',
'djangobower', 'djangobower',
'bootstrap3', # basically for django_admin_bootstrapped 'bootstrap3',
'crispy_forms', 'crispy_forms',
'easy_thumbnails',
'image_cropping',
'floppyforms', 'floppyforms',
'haystack',
'sass_processor', 'sass_processor',
'rest_framework', 'rest_framework',
'django_filters',
'easy_thumbnails',
'image_cropping',
'reversion', 'reversion',
'reversion_compare', 'reversion_compare',
'haystack',
'whoosh', 'whoosh',
'speedinfo', 'speedinfo',
@ -110,7 +111,7 @@ SOLR_URL = config('SOLR_URL', cast=str, default='http://localhost:8983')
SOLR_COLLECTION = config('SOLR_COLLECTION', cast=str, default='sapl') SOLR_COLLECTION = config('SOLR_COLLECTION', cast=str, default='sapl')
if USE_SOLR: if USE_SOLR:
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' #enable auto-index HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' # enable auto-index
SEARCH_BACKEND = 'haystack.backends.solr_backend.SolrEngine' SEARCH_BACKEND = 'haystack.backends.solr_backend.SolrEngine'
SEARCH_URL = ('URL', '{}/solr/{}'.format(SOLR_URL, SOLR_COLLECTION)) SEARCH_URL = ('URL', '{}/solr/{}'.format(SOLR_URL, SOLR_COLLECTION))
@ -118,7 +119,7 @@ if USE_SOLR:
HAYSTACK_CONNECTIONS = { HAYSTACK_CONNECTIONS = {
'default': { 'default': {
'ENGINE': SEARCH_BACKEND, 'ENGINE': SEARCH_BACKEND,
SEARCH_URL[0]: SEARCH_URL[1], SEARCH_URL[0]: SEARCH_URL[1],
'BATCH_SIZE': 1000, 'BATCH_SIZE': 1000,
'TIMEOUT': 60, 'TIMEOUT': 60,
}, },
@ -166,7 +167,7 @@ REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "sapl.api.pagination.StandardPagination", "DEFAULT_PAGINATION_CLASS": "sapl.api.pagination.StandardPagination",
"DEFAULT_FILTER_BACKENDS": ( "DEFAULT_FILTER_BACKENDS": (
"rest_framework.filters.SearchFilter", "rest_framework.filters.SearchFilter",
"rest_framework.filters.DjangoFilterBackend", 'django_filters.rest_framework.DjangoFilterBackend',
), ),
} }
@ -282,6 +283,7 @@ DAB_FIELD_RENDERER = \
CRISPY_TEMPLATE_PACK = 'bootstrap3' CRISPY_TEMPLATE_PACK = 'bootstrap3'
CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap3' CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap3'
CRISPY_FAIL_SILENTLY = not DEBUG CRISPY_FAIL_SILENTLY = not DEBUG
FLOPPY_FORMS_USE_GIS = False
BOWER_COMPONENTS_ROOT = PROJECT_DIR.child("bower") BOWER_COMPONENTS_ROOT = PROJECT_DIR.child("bower")
BOWER_INSTALLED_APPS = ( BOWER_INSTALLED_APPS = (
@ -339,6 +341,22 @@ LOGGING = {
} }
} }
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher', # default
'sapl.hashers.ZopeSHA1PasswordHasher',
]
def remove_warnings():
import warnings
warnings.filterwarnings(
'ignore', module='floppyforms',
message='Unable to import floppyforms.gis'
)
remove_warnings()
def uncaught_exceptions(type, value, error_traceback): def uncaught_exceptions(type, value, error_traceback):
import traceback import traceback
@ -350,8 +368,3 @@ def uncaught_exceptions(type, value, error_traceback):
# captura exceções que não foram tratadas # captura exceções que não foram tratadas
sys.excepthook = uncaught_exceptions sys.excepthook = uncaught_exceptions
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher', # default
'sapl.hashers.ZopeSHA1PasswordHasher',
]

3
sapl/static/js/app.js

@ -47,6 +47,7 @@ function refreshMask() {
$('.dateinput').mask('00/00/0000', {placeholder:"__/__/____"}); $('.dateinput').mask('00/00/0000', {placeholder:"__/__/____"});
$('.hora').mask("00:00", {placeholder:"hh:mm"}); $('.hora').mask("00:00", {placeholder:"hh:mm"});
$('.hora_hms').mask("00:00:00", {placeholder:"hh:mm:ss"}); $('.hora_hms').mask("00:00:00", {placeholder:"hh:mm:ss"});
$('.timeinput').mask("00:00:00", {placeholder:"hh:mm:ss"});
$('.cronometro').mask("00:00:00", {placeholder:"hh:mm:ss"}); $('.cronometro').mask("00:00:00", {placeholder:"hh:mm:ss"});
} }
@ -189,7 +190,7 @@ function OptionalCustomFrontEnd() {
if (_label.length === 0) { if (_label.length === 0) {
_label = $('label[for='+this.id+']'); _label = $('label[for='+this.id+']');
if (_label.length === 0) { if (_label.length === 0) {
_label = $('<label[for='+this.id+']/>').insertBefore(this) _label = $('<label for='+this.id+'/>').insertBefore(this)
} }
} }

3
sapl/static/styles/app.scss

@ -451,6 +451,9 @@ nav {
overflow-x: auto; overflow-x: auto;
} }
} }
.lista-parlamentares .table td {
vertical-align: middle;
}
@media (min-width: 1092px) and (max-width: 1199px) { @media (min-width: 1092px) and (max-width: 1199px) {
.container { .container {

22
sapl/temp_suppress_crispy_form_warnings.py

@ -1,22 +0,0 @@
import copy
import logging
from django.utils.log import DEFAULT_LOGGING
# hack to suppress many annoying warnings from crispy_forms
# Do remove this file and corresponding import in settings
# when crispy_forms is corrected !!!
SUPRESS_CRISPY_FORM_WARNINGS_LOGGING = copy.deepcopy(DEFAULT_LOGGING)
SUPRESS_CRISPY_FORM_WARNINGS_LOGGING['filters']['suppress_deprecated'] = {
'()': 'sapl.temp_suppress_crispy_form_warnings.SuppressDeprecated'
}
SUPRESS_CRISPY_FORM_WARNINGS_LOGGING['handlers']['console']['filters'].append(
'suppress_deprecated')
class SuppressDeprecated(logging.Filter):
def filter(self, record):
msg = record.getMessage()
return not ('crispy_forms' in msg and
'RemovedInDjango19Warning' in msg)

2
sapl/templates/base.html

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

4
sapl/templates/compilacao/tipotextoarticulado_list.html

@ -15,7 +15,7 @@
{% endblock actions %} {% endblock actions %}
<div class="row container-tabaux"> <div class="row container-tabaux">
<div class="{% if perms.base.menu_tabelas_auxiliares %}col-sm-9{% endif %}"> <div class="{% if perms.base.view_tabelas_auxiliares %}col-sm-9{% endif %}">
{% if not object_list %} {% if not object_list %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
@ -40,7 +40,7 @@
{%endif%} {%endif%}
{% include 'paginacao.html'%} {% include 'paginacao.html'%}
</div> </div>
{% if perms.base.menu_tabelas_auxiliares %} {% if perms.base.view_tabelas_auxiliares %}
<div class="col-sm-3 sidebar-tabaux"> <div class="col-sm-3 sidebar-tabaux">
<h3>{% trans "Tabelas Auxiliares" %}</h3> <h3>{% trans "Tabelas Auxiliares" %}</h3>
{% subnav 'menu_tabelas_auxiliares.yaml'%} {% subnav 'menu_tabelas_auxiliares.yaml'%}

4
sapl/templates/crud/list_tabaux.html

@ -2,10 +2,10 @@
{% load i18n menus%} {% load i18n menus%}
{% block base_content %} {% block base_content %}
<div class="row container-tabaux"> <div class="row container-tabaux">
<div class="{% if perms.base.menu_tabelas_auxiliares %}col-sm-9{% endif %}"> <div class="{% if perms.base.view_tabelas_auxiliares %}col-sm-9{% endif %}">
{{block.super}} {{block.super}}
</div> </div>
{% if perms.base.menu_tabelas_auxiliares %} {% if perms.base.view_tabelas_auxiliares %}
<div class="col-sm-3 sidebar-tabaux"> <div class="col-sm-3 sidebar-tabaux">
<h3>{% trans "Tabelas Auxiliares" %}</h3> <h3>{% trans "Tabelas Auxiliares" %}</h3>
{% subnav 'menu_tabelas_auxiliares.yaml'%} {% subnav 'menu_tabelas_auxiliares.yaml'%}

22
sapl/templates/parlamentares/frente_form.html

@ -26,7 +26,8 @@
<div class="row-fluid"> <div class="row-fluid">
<div class="col-md-12" > <div class="col-md-12" >
<div class="checkbox"> <div class="checkbox">
<input id="id_selecionar_ativos" type="checkbox"> <label for="id_selecionar_ativos">
<input id="id_selecionar_ativos" type="checkbox" name="id_selecionar_ativos"/>
<b><h4>Listar somente os parlamentares ativos</h4></b> <b><h4>Listar somente os parlamentares ativos</h4></b>
</div> </div>
</div> </div>
@ -57,16 +58,15 @@
function selecionar_parlamentares_frente() { function selecionar_parlamentares_frente() {
// Seleciona automaticamente todos os parlamentares // Seleciona automaticamente todos os parlamentares
// que já estão presentes naquela frente // que já estão presentes naquela frente
var update_view = {{ update_view }} {% if object.id %}
if (update_view == 1) { var frente_id = {{ object.id }}
var frente_id = {{ object.id }} $.get("/sistema/frente/parlamentares-frente-selected",
$.get("/sistema/frente/parlamentares-frente-selected", {frente_id: frente_id},
{frente_id: frente_id}, function (data) {
function (data) { id_list = data['id_list'];
id_list = data['id_list']; $("#id_parlamentares").val(id_list);
$("div.controls select").val(id_list); });
}); {% endif %}
}
} }
function atualiza_parlamentares() { function atualiza_parlamentares() {

6
sapl/templates/parlamentares/parlamentares_list.html

@ -21,7 +21,7 @@
{% if not rows %} {% if not rows %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<div class="container-table"> <div class="container-table lista-parlamentares">
<div class="result-count">{% blocktrans with verbose_name_plural=view.verbose_name_plural %}Total de {{ verbose_name_plural }}: <strong>{{count}}</strong>{% endblocktrans %}</div> <div class="result-count">{% blocktrans with verbose_name_plural=view.verbose_name_plural %}Total de {{ verbose_name_plural }}: <strong>{{count}}</strong>{% endblocktrans %}</div>
<table class="table table-striped table-hover table-link-ordering"> <table class="table table-striped table-hover table-link-ordering">
<thead> <thead>
@ -43,7 +43,9 @@
{% for value, href, obj in value_list %} {% for value, href, obj in value_list %}
{% if forloop.first %} {% if forloop.first %}
<td> <td>
<img class="avatar-parlamentar" src="{% cropped_thumbnail obj "cropping" %}"> {% if obj.fotografia %}
<img class="avatar-parlamentar" src="{% cropped_thumbnail obj "cropping" %}">
{% endif %}
</td> </td>
{% endif %} {% endif %}
<td> <td>

6
sapl/templates/protocoloadm/comprovante.html

@ -65,7 +65,11 @@
</tr> </tr>
<tr> <tr>
<th>Data / Horário</th> <th>Data / Horário</th>
<td>{{ protocolo.timestamp|date:"d/m/Y" }} - {{ protocolo.timestamp|date:"H:i:s" }}</td> {% if protocolo.timestamp %}
<td>{{ protocolo.timestamp|date:"d/m/Y" }} - {{ protocolo.timestamp|date:"H:i:s" }}</td>
{% else %}
<td>{{ protocolo.data|date:"d/m/Y" }} - {{ protocolo.hora|date:"H:i:s" }}</td>
{% endif %}
</tr> </tr>
{% if protocolo.tipo_processo == 1 %} {% if protocolo.tipo_processo == 1 %}
<tr> <tr>

14
sapl/templates/protocoloadm/protocolar_documento.html

@ -14,3 +14,17 @@
{% block detail_content %} {% block detail_content %}
{% crispy form %} {% crispy form %}
{% endblock detail_content %} {% endblock detail_content %}
{% block extra_js %}
<script language="Javascript">
$(document).ready(function() {
$("input[name=data_hora_manual]").change(function(event) {
if (this.value === 'True' && this.checked)
$("#protocolo_data_hora_manual").removeClass('hidden');
else if (this.value === 'False' && this.checked)
$("#protocolo_data_hora_manual").addClass('hidden');
});
$("input[name=data_hora_manual]").trigger('change')
});
</script>
{% endblock %}

62
sapl/templates/protocoloadm/protocolar_materia.html

@ -26,35 +26,43 @@
} }
$(document).ready(function() { $(document).ready(function() {
function busca_ementa() { $("input[name=data_hora_manual]").change(function(event) {
var vincular_materia = $("#id_vincular_materia_1").prop("checked"); if (this.value === 'True' && this.checked)
var ano_materia = $("#id_ano_materia").val(); $("#protocolo_data_hora_manual").removeClass('hidden');
var numero_materia = $("#id_numero_materia").val(); else if (this.value === 'False' && this.checked)
var tipo_materia = $("#id_tipo_materia").val(); $("#protocolo_data_hora_manual").addClass('hidden');
var json_data = { });
ano : ano_materia, $("input[name=data_hora_manual]").trigger('change')
numero : numero_materia,
tipo : tipo_materia
}
if (vincular_materia === true && ano_materia !== undefined &&
numero_materia !== undefined && numero_materia !== "") {
$.getJSON("/protocoloadm/recuperar-materia", json_data, function(data){
if (data) {
if (data['error'] === undefined){
$('#id_assunto_ementa').val(data['ementa']);
if (data['autor'] !== undefined) {
$('#id_autor').val(data['autor']);
$('#id_tipo_autor').val(data['tipo_autor']);
} function busca_ementa() {
} var vincular_materia = $("#id_vincular_materia_1").prop("checked");
} var ano_materia = $("#id_ano_materia").val();
}) var numero_materia = $("#id_numero_materia").val();
} var tipo_materia = $("#id_tipo_materia").val();
var json_data = {
ano : ano_materia,
numero : numero_materia,
tipo : tipo_materia
}
if (vincular_materia === true && ano_materia !== undefined &&
numero_materia !== undefined && numero_materia !== "") {
$.getJSON("/protocoloadm/recuperar-materia", json_data, function(data){
if (data) {
if (data['error'] === undefined){
$('#id_assunto_ementa').val(data['ementa']);
if (data['autor'] !== undefined) {
$('#id_autor').val(data['autor']);
$('#id_tipo_autor').val(data['tipo_autor']);
}
}
}
})
}
}; };
$("#id_ano_materia").blur(busca_ementa); $("#id_ano_materia").blur(busca_ementa);
$("#id_numero_materia").blur(busca_ementa); $("#id_numero_materia").blur(busca_ementa);
$("#id_tipo_materia").change(busca_ementa); $("#id_tipo_materia").change(busca_ementa);
$("#id_tipo_autor").change(function() { $("#id_tipo_autor").change(function() {
var tipo_selecionado = $("#id_tipo_autor").val(); var tipo_selecionado = $("#id_tipo_autor").val();

2
sapl/templates/protocoloadm/protocolo_filter.html

@ -47,7 +47,7 @@
{% if p.timestamp%} {% if p.timestamp%}
<strong>Data Protocolo:</strong> {{ p.timestamp|localtime|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ p.timestamp|localtime|date:"G:i:s" }}</br> <strong>Data Protocolo:</strong> {{ p.timestamp|localtime|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ p.timestamp|localtime|date:"G:i:s" }}</br>
{% else %} {% else %}
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ p.hora|date:"G:i:s" }}</br> <strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ p.hora|date:"G:i:s" }} - {% if not p.timestamp %} Informado Manualmente por: {{p.user_data_hora_manual}}{% endif %}</br>
{% endif %} {% endif %}
{% if p.tipo_processo == 0 %} {% if p.tipo_processo == 0 %}
<strong>Interessado:</strong> {{ p.interessado|default_if_none:"Não informado" }}</br> <strong>Interessado:</strong> {{ p.interessado|default_if_none:"Não informado" }}</br>

10
sapl/templates/protocoloadm/protocolo_mostrar.html

@ -4,6 +4,14 @@
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% load static %} {% load static %}
{% block actions %}
{{ block.super }}
<div class="actions btn-group pull-right grid-gutter-width-right " role="group">
<a href="{% url 'sapl.protocoloadm:protocolo' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a>
</div>
{% endblock %}
{% block detail_content %} {% block detail_content %}
<strong>Protocolo: </strong>{{ protocolo.numero|stringformat:'06d' }}/{{ protocolo.ano }} - <strong>Protocolo: </strong>{{ protocolo.numero|stringformat:'06d' }}/{{ protocolo.ano }} -
<a href="{% url 'sapl.relatorios:relatorio_etiqueta_protocolo' protocolo.numero protocolo.ano %}"><img src="{% static 'img/etiqueta.png' %}" alt="Etiqueta Individual"></a></br> <a href="{% url 'sapl.relatorios:relatorio_etiqueta_protocolo' protocolo.numero protocolo.ano %}"><img src="{% static 'img/etiqueta.png' %}" alt="Etiqueta Individual"></a></br>
@ -12,7 +20,7 @@
{% if protocolo.timestamp %} {% if protocolo.timestamp %}
<strong>Data Protocolo:</strong> {{ protocolo.timestamp|localtime|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ protocolo.timestamp|localtime|date:"G:i:s" }}</br> <strong>Data Protocolo:</strong> {{ protocolo.timestamp|localtime|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ protocolo.timestamp|localtime|date:"G:i:s" }}</br>
{% else %} {% else %}
<strong>Data Protocolo:</strong> {{ protocolo.data|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ protocolo.hora|date:"G:i:s" }}</br> <strong>Data Protocolo:</strong> {{ protocolo.data|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ protocolo.hora|date:"G:i:s" }} - {% if not protocolo.timestamp %} Informado Manualmente por: {{protocolo.user_data_hora_manual}}{% endif %}</br>
{% endif %} {% endif %}
{% if protocolo.tipo_processo == 0 %} {% if protocolo.tipo_processo == 0 %}

55
sapl/utils.py

@ -18,10 +18,10 @@ from django.core.exceptions import ValidationError
from django.core.mail import get_connection from django.core.mail import get_connection
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from django.forms.widgets import SplitDateTimeWidget
from django.utils import six, timezone from django.utils import six, timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import django_filters import django_filters
from django_filters.filterset import STRICTNESS
from easy_thumbnails import source_generators from easy_thumbnails import source_generators
from floppyforms import ClearableFileInput from floppyforms import ClearableFileInput
import magic import magic
@ -209,24 +209,49 @@ class RangeWidgetOverride(forms.MultiWidget):
def __init__(self, attrs=None): def __init__(self, attrs=None):
widgets = (forms.DateInput(format='%d/%m/%Y', widgets = (forms.DateInput(format='%d/%m/%Y',
attrs={'class': 'dateinput', attrs={'class': 'dateinput form-control',
'placeholder': 'Inicial'}), 'placeholder': 'Inicial'}),
forms.DateInput(format='%d/%m/%Y', forms.DateInput(format='%d/%m/%Y',
attrs={'class': 'dateinput', attrs={'class': 'dateinput form-control',
'placeholder': 'Final'})) 'placeholder': 'Final'}))
super(RangeWidgetOverride, self).__init__(widgets, attrs) super(RangeWidgetOverride, self).__init__(widgets, attrs)
def decompress(self, value): def decompress(self, value):
if value: if value:
return [value.start, value.stop] return [value.start, value.stop]
return [None, None] return []
def render(self, name, value, attrs=None, renderer=None):
rendered_widgets = []
for i, x in enumerate(self.widgets):
rendered_widgets.append(
x.render(
'%s_%d' % (name, i), value[i] if value else ''
)
)
def format_output(self, rendered_widgets):
html = '<div class="col-sm-6">%s</div><div class="col-sm-6">%s</div>'\ html = '<div class="col-sm-6">%s</div><div class="col-sm-6">%s</div>'\
% tuple(rendered_widgets) % tuple(rendered_widgets)
return '<div class="row">%s</div>' % html return '<div class="row">%s</div>' % html
class CustomSplitDateTimeWidget(SplitDateTimeWidget):
def render(self, name, value, attrs=None, renderer=None):
rendered_widgets = []
for i, x in enumerate(self.widgets):
x.attrs['class'] += ' form-control'
rendered_widgets.append(
x.render(
'%s_%d' % (name, i), self.decompress(
value)[i] if value else ''
)
)
html = '<div class="col-6">%s</div><div class="col-6">%s</div>'\
% tuple(rendered_widgets)
return '<div class="row">%s</div>' % html
def register_all_models_in_admin(module_name, exclude_list=[]): def register_all_models_in_admin(module_name, exclude_list=[]):
appname = module_name.split('.') appname = module_name.split('.')
appname = appname[1] if appname[0] == 'sapl' else appname[0] appname = appname[1] if appname[0] == 'sapl' else appname[0]
@ -685,12 +710,12 @@ def qs_override_django_filter(self):
valid = self.is_bound and self.form.is_valid() valid = self.is_bound and self.form.is_valid()
if self.is_bound and not valid: if self.is_bound and not valid:
if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR: """if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
raise forms.ValidationError(self.form.errors) raise forms.ValidationError(self.form.errors)
elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS: elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS:"""
self._qs = self.queryset.none() self._qs = self.queryset.none()
return self._qs return self._qs
# else STRICTNESS.IGNORE... ignoring # else STRICTNESS.IGNORE... ignoring
# start with all the results and filter from there # start with all the results and filter from there
qs = self.queryset.all() qs = self.queryset.all()
@ -703,12 +728,12 @@ def qs_override_django_filter(self):
try: try:
value = self.form.fields[name].clean(raw_value) value = self.form.fields[name].clean(raw_value)
except forms.ValidationError: except forms.ValidationError:
if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR: """if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
raise raise
elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS: elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS:"""
self._qs = self.queryset.none() self._qs = self.queryset.none()
return self._qs return self._qs
# else STRICTNESS.IGNORE... ignoring # else STRICTNESS.IGNORE... ignoring
if value is not None: # valid & clean data if value is not None: # valid & clean data
qs = qs._next_is_sticky() qs = qs._next_is_sticky()

58
setup.py

@ -9,47 +9,49 @@ with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme:
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
install_requires = [ install_requires = [
'dj-database-url==0.4.1', 'django>=1.11,<2.0',
'django-haystack==2.6.0', 'django-bootstrap3==11.0.0',
'django>=1.10,<1.11', 'django-haystack==2.8.1',
'django-bootstrap3==7.0.1', 'django-filter==2.0.0',
'djangorestframework==3.9.0',
'dj-database-url==0.5.0',
'django-bower==5.2.0', 'django-bower==5.2.0',
'django-braces==1.9.0', 'django-braces==1.9.0',
'django-compressor==2.0', 'django-crispy-forms==1.7.2',
'django-crispy-forms==1.6.1', 'django-floppyforms==1.7.0',
'django-extensions==1.9.8', 'django-extra-views==0.12.0',
'django-extra-views==0.11.0', 'django-model-utils==3.1.2',
'django-filter==0.15.3', 'django-sass-processor==0.7.2',
'django-floppyforms==1.6.2', 'django-reversion==3.0.2',
'django-model-utils==3.1.1', 'django-reversion-compare==0.8.6'
'django-sass-processor==0.5.8', 'django-speedinfo==1.4.0',
'djangorestframework==3.4.0', 'django-extensions==2.1.4',
'drfdocs', 'django-image-cropping==1.2.0',
'easy-thumbnails==2.5', 'easy-thumbnails==2.5',
'django-image-cropping==1.1.0', 'libsass==0.17.0',
'libsass==0.11.1', 'python-decouple==3.1',
'psycopg2==2.7.4', 'psycopg2-binary==2.7.6.1',
'python-decouple==3.0',
'pytz==2016.4',
'pyyaml==4.2b1', 'pyyaml==4.2b1',
'rtyaml==0.0.3', 'pytz==2018.9',
'textract==1.5.0', 'rtyaml==0.0.5',
'python-magic==0.4.15',
'unipath==1.1', 'unipath==1.1',
'WeasyPrint==44',
'gunicorn==19.9.0',
'textract==1.5.0',
'pysolr==3.6.0', 'pysolr==3.6.0',
'python-magic==0.4.12',
'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'
# 'git+git://github.com/interlegis/trml2pdf.git', # 'git+git://github.com/interlegis/trml2pdf.git',
# 'git+git://github.com/jasperlittle/django-rest-framework-docs' # 'git+git://github.com/jasperlittle/django-rest-framework-docs'
# 'git+git://github.com/rubgombar1/django-admin-bootstrapped.git'' # 'git+git://github.com/rubgombar1/django-admin-bootstrapped.git''
#'django-compressor==2.2',
] ]
setup( setup(
name='interlegis-sapl', name='interlegis-sapl',
version='3.1.140', version='3.1.142',
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007', license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007',

Loading…
Cancel
Save