diff --git a/base/forms.py b/base/forms.py index 53b7f4d32..6b05ee59b 100644 --- a/base/forms.py +++ b/base/forms.py @@ -4,19 +4,15 @@ from django import forms from django.core.exceptions import ValidationError from django.forms import ModelForm from django.utils.translation import ugettext_lazy as _ -from floppyforms import ClearableFileInput import crispy_layout_mixin +import sapl from crispy_layout_mixin import form_actions from sapl.settings import MAX_IMAGE_UPLOAD_SIZE from .models import CasaLegislativa -class ImageThumbnailFileInput(ClearableFileInput): - template_name = 'floppyforms/image_thumbnail.html' - - class CasaLegislativaTabelaAuxForm(ModelForm): class Meta: @@ -41,9 +37,9 @@ class CasaLegislativaTabelaAuxForm(ModelForm): 'cep': forms.TextInput(attrs={'class': 'cep'}), 'telefone': forms.TextInput(attrs={'class': 'telefone'}), 'fax': forms.TextInput(attrs={'class': 'telefone'}), - 'logotipo': ImageThumbnailFileInput, + 'logotipo': sapl.utils.ImageThumbnailFileInput, 'informacao_geral': forms.Textarea( - attrs={'id': 'casa-informacoes'}) + attrs={'id': 'texto-rico'}) } def clean_logotipo(self): diff --git a/crispy_layout_mixin.py b/crispy_layout_mixin.py index 7971466ca..89ce0048f 100644 --- a/crispy_layout_mixin.py +++ b/crispy_layout_mixin.py @@ -5,6 +5,7 @@ import rtyaml from crispy_forms.bootstrap import FormActions from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Div, Fieldset, Layout, Submit +from django.utils import formats from django.utils.translation import ugettext as _ @@ -57,9 +58,18 @@ def get_field_display(obj, fieldname): if value is None: display = '' elif 'date' in str(type(value)): - display = value.strftime("%d/%m/%Y") # TODO: localize + display = formats.date_format(value, "SHORT_DATE_FORMAT") elif 'bool' in str(type(value)): - display = 'Sim' if value else 'Não' + display = _('Sim') if value else _('Não') + elif 'ImageFieldFile' in str(type(value)): + display = ''.format(value.url) + elif 'FieldFile' in str(type(value)): + if value: + display = '{}'.format( + value.url, + value.name.split('/')[-1:][0]) + else: + display = '' else: display = str(value) return verbose_name, display diff --git a/crud/tests/test_base.py b/crud/tests/test_base.py index 824857a91..0982b3d25 100644 --- a/crud/tests/test_base.py +++ b/crud/tests/test_base.py @@ -87,7 +87,7 @@ def test_layout_fieldnames(_layout, result): assert view.list_field_names == result -def test_layout_detail_fieldsets(monkeypatch): +def test_layout_detail_fieldsets(): stub = mommy.make(Country, name='Brazil', @@ -183,7 +183,7 @@ def assert_on_detail_page(res, stub_name): (9, 4, [(0, 4), (4, 8), (8, 9)], ['Anterior', '1', '2', '3', 'Próxima']), ]) def test_flux_list_paginate_detail( - app, monkeypatch, num_entries, page_size, ranges, page_list): + app, num_entries, page_size, ranges, page_list): entries_labels = [] for i in range(num_entries): diff --git a/legacy/migration.py b/legacy/migration.py index d98b17b19..f1291ca16 100644 --- a/legacy/migration.py +++ b/legacy/migration.py @@ -27,6 +27,7 @@ appconfs = [apps.get_app_config(n) for n in [ 'protocoloadm', ]] stubs_list = [] +unique_constraints = [] name_sets = [set(m.__name__ for m in ac.get_models()) for ac in appconfs] @@ -93,7 +94,6 @@ def warn(msg): def get_fk_related(field, value, label=None): fields_dict = {} - if value is None and field.null is False: value = 0 if value is not None: @@ -105,7 +105,7 @@ def get_fk_related(field, value, label=None): field.name, value, field.model.__name__, label or '---') if value == 0: - # se FK == 0, criamos um stub e colocamos o valor '???????? + # se FK == 0, criamos um stub e colocamos o valor '????????' # para qualquer CharField ou TextField que possa haver if not field.null: all_fields = field.related_model._meta.get_fields() @@ -149,6 +149,37 @@ def iter_sql_records(sql, db): yield record +def delete_constraints(model): + # pega nome da unique constraint dado o nome da tabela + table = model._meta.db_table + cursor = exec_sql("SELECT conname FROM pg_constraint WHERE conrelid = " + "(SELECT oid FROM pg_class WHERE relname LIKE " + "'%s') and contype = 'u';" % (table)) + result = cursor.fetchone() + # se existir um resultado, unique constraint será deletado + if result: + warn('Excluindo unique constraint de nome %s' % result) + args = model._meta.unique_together[0] + args_list = list(args) + unique_constraints.append([table, result[0], args_list, model]) + exec_sql("ALTER TABLE %s DROP CONSTRAINT %s;" % + (table, result[0])) + + +def recreate_constraints(): + if unique_constraints: + for constraint in unique_constraints: + table, name, args, model = constraint + for i in range(len(args)): + if isinstance(model._meta.get_field(args[i]), + models.ForeignKey): + args[i] = args[i]+'_id' + args_string = '' + args_string += "(" + ', '.join(map(str, args)) + ")" + exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" % + (table, name, args_string)) + + def save_with_id(new, id): sequence_name = '%s_id_seq' % type(new)._meta.db_table cursor = exec_sql('SELECT last_value from %s;' % sequence_name) @@ -169,7 +200,6 @@ def make_stub(model, id): class DataMigrator: - def __init__(self): self.field_renames, self.model_renames = get_renames() @@ -179,6 +209,8 @@ class DataMigrator: for field in new._meta.fields: old_field_name = renames.get(field.name) field_type = field.get_internal_type() + msg = ("Campo %s (%s) da model %s " % + (field.name, field_type, field.model.__name__)) if old_field_name: old_value = getattr(old, old_field_name) if isinstance(field, models.ForeignKey): @@ -187,17 +219,29 @@ class DataMigrator: old_type._meta.pk.name != 'id': label = old.pk else: - label = '-- WITHOUT PK --' + label = '-- SEM PK --' value = get_fk_related(field, old_value, label) else: value = getattr(old, old_field_name) + if (field_type == 'DateField' and + field.null is False and value is None): + names = [old_fields.name for old_fields + in old._meta.get_fields()] + combined_names = "(" + ")|(".join(names) + ")" + matches = re.search('(ano_\w+)', combined_names) + if not matches: + warn(msg + + '=> colocando valor 0000-01-01 para DateField') + value = '0001-01-01' + else: + value = '%d-01-01' % getattr(old, matches.group(0)) + warn(msg + + "=> colocando %s para DateField não nulável" % + (value)) if field_type == 'CharField' or field_type == 'TextField': if value is None: - warn( - "Field %s (%s) from model %s" - " => settig empty string '' for %s value" % - (field.name, field_type, field.model.__name__, - value)) + warn(msg + "=> colocando string vazia para valor %s" % + (value)) value = '' setattr(new, field.name, value) @@ -205,14 +249,16 @@ class DataMigrator: # warning: model/app migration order is of utmost importance self.to_delete = [] - info('Starting %s migration...' % obj) + info('Começando migração: %s...' % obj) self._do_migrate(obj) # exclude logically deleted in legacy base - info('Deleting models with ind_excluido...') + info('Deletando models com ind_excluido...') for obj in self.to_delete: obj.delete() - info('Deleting unnecessary stubs...') + info('Deletando stubs desnecessários...') self.delete_stubs() + info('Recriando unique constraints...') + recreate_constraints() def _do_migrate(self, obj): if isinstance(obj, AppConfig): @@ -229,7 +275,7 @@ class DataMigrator: 'Parameter must be a Model, AppConfig or a sequence of them') def migrate_model(self, model): - print('Migrating %s...' % model.__name__) + print('Migrando %s...' % model.__name__) legacy_model_name = self.model_renames.get(model, model.__name__) legacy_model = legacy_app.get_model(legacy_model_name) @@ -238,6 +284,7 @@ class DataMigrator: # Clear all model entries # They may have been created in a previous migration attempt model.objects.all().delete() + delete_constraints(model) # setup migration strategy for tables with or without a pk if legacy_pk_name == 'id': @@ -304,7 +351,7 @@ def adjust_parlamentar(new_parlamentar, old): # but data includes null values # => transform None to False if value is None: - warn('null converted to False') + warn('nulo convertido para falso') new_parlamentar.unidade_deliberativa = False diff --git a/materia/forms.py b/materia/forms.py index a5ae348b2..6f6587c7e 100644 --- a/materia/forms.py +++ b/materia/forms.py @@ -1,7 +1,10 @@ +import django_filters from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Submit from django import forms from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.db import models +from django.db.models import Max from django.forms import ModelForm from django.utils.translation import ugettext_lazy as _ @@ -10,14 +13,14 @@ import sapl from crispy_layout_mixin import form_actions from norma.models import LegislacaoCitada, TipoNormaJuridica from sapl.settings import MAX_DOC_UPLOAD_SIZE +from sapl.utils import RANGE_ANOS from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria, DespachoInicial, DocumentoAcessorio, MateriaLegislativa, - Numeracao, Proposicao, Relatoria, StatusTramitacao, - TipoMateriaLegislativa, Tramitacao, UnidadeTramitacao) + Numeracao, Proposicao, Relatoria, TipoMateriaLegislativa, + Tramitacao) -ORDENACAO_MATERIAIS = [(1, 'Crescente'), - (2, 'Decrescente')] +ANO_CHOICES = [('', '---------')] + RANGE_ANOS def em_tramitacao(): @@ -140,13 +143,13 @@ class DocumentoAcessorioForm(ModelForm): [('tipo', 4), ('nome', 4), ('data', 4)]) row2 = crispy_layout_mixin.to_row( - [('autor', 0), - (Button('pesquisar', - 'Pesquisar Autor', - css_class='btn btn-primary btn-sm'), 2), - (Button('limpar', - 'Limpar Autor', - css_class='btn btn-primary btn-sm'), 10)]) + [('autor', 0), + (Button('pesquisar', + 'Pesquisar Autor', + css_class='btn btn-primary btn-sm'), 2), + (Button('limpar', + 'Limpar Autor', + css_class='btn btn-primary btn-sm'), 10)]) row3 = crispy_layout_mixin.to_row( [('ementa', 12)]) @@ -192,7 +195,7 @@ class TramitacaoForm(ModelForm): label='Tramitando', choices=[(True, 'Sim'), (False, 'Não')], widget=forms.Select( - attrs={'class': 'selector'})) + attrs={'class': 'selector'})) class Meta: model = Tramitacao @@ -455,75 +458,90 @@ class AutoriaForm(ModelForm): *args, **kwargs) -class MateriaLegislativaPesquisaForm(ModelForm): +class RangeWidgetOverride(forms.MultiWidget): - autor = forms.CharField(widget=forms.HiddenInput(), required=False) + def __init__(self, attrs=None): + widgets = (forms.DateInput(format='%d/%m/%Y', + attrs={'class': 'dateinput', + 'placeholder': 'Inicial'}), + forms.DateInput(format='%d/%m/%Y', + attrs={'class': 'dateinput', + 'placeholder': 'Final'})) + super(RangeWidgetOverride, self).__init__(widgets, attrs) - localizacao = forms.ModelChoiceField( - label='Localização Atual', - required=False, - queryset=UnidadeTramitacao.objects.all(), - empty_label='Selecione', - ) + def decompress(self, value): + if value: + return [value.start, value.stop] + return [None, None] - situacao = forms.ModelChoiceField( - label='Situação', - required=False, - queryset=StatusTramitacao.objects.all(), - empty_label='Selecione', - ) + def format_output(self, rendered_widgets): + return ''.join(rendered_widgets) + + +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')), + 'widget': RangeWidgetOverride} + }} - em_tramitacao = forms.ChoiceField(required=False, - label='Tramitando', - choices=em_tramitacao(), - widget=forms.Select( - attrs={'class': 'selector'})) - - publicacao_inicial = forms.DateField(label=u'Data Publicação Inicial', - input_formats=['%d/%m/%Y'], - required=False, - widget=forms.DateInput( - format='%d/%m/%Y', - attrs={'class': 'dateinput'})) - - publicacao_final = forms.DateField(label=u'Data Publicação Final', - input_formats=['%d/%m/%Y'], - required=False, - widget=forms.DateInput( - format='%d/%m/%Y', - attrs={'class': 'dateinput'})) - - apresentacao_inicial = forms.DateField(label=u'Data Apresentação Inicial', - input_formats=['%d/%m/%Y'], - required=False, - widget=forms.DateInput( - format='%d/%m/%Y', - attrs={'class': 'dateinput'})) - - apresentacao_final = forms.DateField(label=u'Data Apresentação Final', - input_formats=['%d/%m/%Y'], - required=False, - widget=forms.DateInput( - format='%d/%m/%Y', - attrs={'class': 'dateinput'})) + ano = django_filters.ChoiceFilter(required=False, + label=u'Ano da Matéria', + choices=ANO_CHOICES) + + autoria__autor = django_filters.CharFilter(widget=forms.HiddenInput()) + + ementa = django_filters.CharFilter(lookup_expr='icontains') class Meta: model = MateriaLegislativa - fields = ['tipo', - 'numero', - 'ano', + fields = ['numero', 'numero_protocolo', - 'apresentacao_inicial', - 'apresentacao_final', - 'publicacao_inicial', - 'publicacao_final', - 'autor', + 'ano', + 'tipo', + 'data_apresentacao', + 'data_publicacao', + 'autoria__autor__tipo', + 'autoria__partido', + 'relatoria__parlamentar_id', 'local_origem_externa', - 'localizacao', + 'tramitacao__unidade_tramitacao_destino', + 'tramitacao__status', 'em_tramitacao', - 'situacao'] + ] + + order_by = ( + ('', 'Selecione'), + ('dataC', 'Data, Tipo, Ano, Numero - Ordem Crescente'), + ('dataD', 'Data, Tipo, Ano, Numero - Ordem Decrescente'), + ('tipoC', 'Tipo, Ano, Numero, Data - Ordem Crescente'), + ('tipoD', 'Tipo, Ano, Numero, Data - Ordem Decrescente') + ) + + order_by_mapping = { + '': [], + 'dataC': ['data_apresentacao', 'tipo__sigla', 'ano', 'numero'], + 'dataD': ['-data_apresentacao', '-tipo__sigla', '-ano', '-numero'], + 'tipoC': ['tipo__sigla', 'ano', 'numero', 'data_apresentacao'], + 'tipoD': ['-tipo__sigla', '-ano', '-numero', '-data_apresentacao'], + } + + def get_order_by(self, order_value): + if order_value in self.order_by_mapping: + return self.order_by_mapping[order_value] + else: + return super(MateriaLegislativaFilterSet, + self).get_order_by(order_value) def __init__(self, *args, **kwargs): + super(MateriaLegislativaFilterSet, self).__init__(*args, **kwargs) + + self.filters['tipo'].label = 'Tipo de Matéria' + self.filters['autoria__autor__tipo'].label = 'Tipo de Autor' + self.filters['autoria__partido'].label = 'Partido do Autor' + self.filters['relatoria__parlamentar_id'].label = 'Relatoria' row1 = crispy_layout_mixin.to_row( [('tipo', 12)]) @@ -532,34 +550,73 @@ class MateriaLegislativaPesquisaForm(ModelForm): ('ano', 4), ('numero_protocolo', 4)]) row3 = crispy_layout_mixin.to_row( - [('apresentacao_inicial', 6), - ('apresentacao_final', 6)]) + [('data_apresentacao', 6), + ('data_publicacao', 6)]) row4 = crispy_layout_mixin.to_row( - [('publicacao_inicial', 6), - ('publicacao_final', 6)]) + [('autoria__autor', 0), + (Button('pesquisar', + 'Pesquisar Autor', + css_class='btn btn-primary btn-sm'), 2), + (Button('limpar', + 'limpar Autor', + css_class='btn btn-primary btn-sm'), 10)]) row5 = crispy_layout_mixin.to_row( - [('autor', 0), - (Button('pesquisar', - 'Pesquisar Autor', - css_class='btn btn-primary btn-sm'), 2), - (Button('limpar', - 'limpar Autor', - css_class='btn btn-primary btn-sm'), 10)]) + [('autoria__autor__tipo', 6), + ('autoria__partido', 6)]) row6 = crispy_layout_mixin.to_row( - [('local_origem_externa', 6), - ('localizacao', 6)]) + [('relatoria__parlamentar_id', 6), + ('local_origem_externa', 6)]) row7 = crispy_layout_mixin.to_row( + [('tramitacao__unidade_tramitacao_destino', 6), + ('tramitacao__status', 6)]) + row8 = crispy_layout_mixin.to_row( [('em_tramitacao', 6), - ('situacao', 6)]) + ('o', 6)]) + row9 = crispy_layout_mixin.to_row( + [('ementa', 12)]) - self.helper = FormHelper() - self.helper.layout = Layout( + self.form.helper = FormHelper() + self.form.helper.form_method = 'GET' + self.form.helper.layout = Layout( Fieldset(_('Pesquisa Básica'), - row1, row2, row3, row4, + row1, row2, row3, HTML(sapl.utils.autor_label), HTML(sapl.utils.autor_modal), - row5, row6, row7, + row4, row5, row6, row7, row8, row9, form_actions(save_label='Pesquisar')) ) - super(MateriaLegislativaPesquisaForm, self).__init__( - *args, **kwargs) + + +def pega_ultima_tramitacao(): + ultimas_tramitacoes = Tramitacao.objects.values( + 'materia_id').annotate(data_encaminhamento=Max( + 'data_encaminhamento'), + id=Max('id')).values_list('id') + + lista = [item for sublist in ultimas_tramitacoes for item in sublist] + + return lista + + +def filtra_tramitacao_status(status): + lista = pega_ultima_tramitacao() + return Tramitacao.objects.filter( + id__in=lista, + status=status).distinct().values_list('materia_id', flat=True) + + +def filtra_tramitacao_destino(destino): + lista = pega_ultima_tramitacao() + return Tramitacao.objects.filter( + id__in=lista, + unidade_tramitacao_destino=destino).distinct().values_list( + 'materia_id', flat=True) + + +def filtra_tramitacao_destino_and_status(status, destino): + lista = pega_ultima_tramitacao() + return Tramitacao.objects.filter( + id__in=lista, + status=status, + unidade_tramitacao_destino=destino).distinct().values_list( + 'materia_id', flat=True) diff --git a/materia/test_materia_urls.py b/materia/test_materia_urls.py index 9814612a7..492adde94 100644 --- a/materia/test_materia_urls.py +++ b/materia/test_materia_urls.py @@ -3,9 +3,6 @@ from django.core.urlresolvers import reverse @pytest.mark.parametrize("test_input,kwargs,expected", [ - ('materia:pesquisar_materia_list', - {}, - '/materia/pesquisar-materia-list'), ('materia:relatoria_edit', {'pk': '11', 'id': '99'}, '/materia/11/relatoria/99/edit'), diff --git a/materia/urls.py b/materia/urls.py index 7a7bce314..d39828694 100644 --- a/materia/urls.py +++ b/materia/urls.py @@ -11,8 +11,7 @@ from materia.views import (AcompanhamentoConfirmarView, MateriaLegislativaCrud, MateriaLegislativaPesquisaView, MateriaTaView, NumeracaoEditView, NumeracaoView, OrgaoCrud, - OrigemCrud, PesquisaMateriaListView, - ProposicaoEditView, ProposicaoListView, + OrigemCrud, ProposicaoEditView, ProposicaoListView, ProposicaoTaView, ProposicaoView, RegimeTramitacaoCrud, RelatoriaEditView, RelatoriaView, StatusTramitacaoCrud, TipoAutorCrud, @@ -93,8 +92,6 @@ urlpatterns = [ ProposicaoEditView.as_view(), name='editar_proposicao'), url(r'^materia/pesquisar-materia$', MateriaLegislativaPesquisaView.as_view(), name='pesquisar_materia'), - url(r'^materia/pesquisar-materia-list$', - PesquisaMateriaListView.as_view(), name='pesquisar_materia_list'), url(r'^materia/(?P\d+)/acompanhar-materia/$', AcompanhamentoMateriaView.as_view(), name='acompanhar_materia'), url(r'^materia/(?P\d+)/acompanhar-confirmar$', diff --git a/materia/views.py b/materia/views.py index afe7ede2c..97efe04a7 100644 --- a/materia/views.py +++ b/materia/views.py @@ -12,6 +12,7 @@ from django.shortcuts import redirect from django.template import Context, loader from django.utils.translation import ugettext_lazy as _ from django.views.generic import CreateView, FormView, ListView, TemplateView +from django_filters.views import FilterView import crud.base from base.models import CasaLegislativa @@ -25,8 +26,10 @@ from sapl.utils import get_base_url from .forms import (AcompanhamentoMateriaForm, AutoriaForm, DespachoInicialForm, DocumentoAcessorioForm, LegislacaoCitadaForm, MateriaAnexadaForm, - MateriaLegislativaPesquisaForm, NumeracaoForm, - ProposicaoForm, RelatoriaForm, TramitacaoForm) + MateriaLegislativaFilterSet, NumeracaoForm, ProposicaoForm, + RelatoriaForm, TramitacaoForm, filtra_tramitacao_destino, + filtra_tramitacao_destino_and_status, + filtra_tramitacao_status) from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria, DespachoInicial, DocumentoAcessorio, MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao, RegimeTramitacao, @@ -219,7 +222,7 @@ class MateriaAnexadaEditView(FormView): def get_success_url(self): pk = self.kwargs['pk'] - return reverse('materia:materia_anexada', kwargs={'pk': pk}) + return reverse('materia_anexada', kwargs={'pk': pk}) class DespachoInicialView(CreateView): @@ -1186,118 +1189,78 @@ class ProposicaoListView(ListView): return context -class MateriaLegislativaPesquisaView(FormView): - template_name = 'materia/pesquisa_materia.html' - - def get_success_url(self): - return reverse('materia:pesquisar_materia') - - def get(self, request, *args, **kwargs): - form = MateriaLegislativaPesquisaForm() - return self.render_to_response({'form': form}) - - def post(self, request, *args, **kwargs): - kwargs = {} - form = MateriaLegislativaPesquisaForm(request.POST) +class MateriaLegislativaPesquisaView(FilterView): + model = MateriaLegislativa + filterset_class = MateriaLegislativaFilterSet + paginate_by = 10 - if form.data['tipo']: - kwargs['tipo'] = form.data['tipo'] + def get_filterset_kwargs(self, filterset_class): + super(MateriaLegislativaPesquisaView, + self).get_filterset_kwargs(filterset_class) - if form.data['numero']: - kwargs['numero'] = form.data['numero'] + kwargs = {'data': self.request.GET or None} - if form.data['ano']: - kwargs['ano'] = form.data['ano'] + status_tramitacao = self.request.GET.get('tramitacao__status') + unidade_destino = self.request.GET.get( + 'tramitacao__unidade_tramitacao_destino') - if form.data['numero_protocolo']: - kwargs['numero_protocolo'] = form.data['numero_protocolo'] + qs = self.get_queryset() - if (form.data['apresentacao_inicial'] and - form.data['apresentacao_final']): - kwargs['apresentacao_inicial'] = form.data['apresentacao_inicial'] - kwargs['apresentacao_final'] = form.data['apresentacao_final'] + if status_tramitacao and unidade_destino: + lista = filtra_tramitacao_destino_and_status(status_tramitacao, + unidade_destino) + qs = qs.filter(id__in=lista).distinct() - if (form.data['publicacao_inicial'] and - form.data['publicacao_final']): - kwargs['publicacao_inicial'] = form.data['publicacao_inicial'] - kwargs['publicacao_final'] = form.data['publicacao_final'] + elif status_tramitacao: + lista = filtra_tramitacao_status(status_tramitacao) + qs = qs.filter(id__in=lista).distinct() - if form.data['local_origem_externa']: - kwargs['local_origem_externa'] = form.data['local_origem_externa'] + elif unidade_destino: + lista = filtra_tramitacao_destino(unidade_destino) + qs = qs.filter(id__in=lista).distinct() - if form.data['autor']: - kwargs['autor'] = form.data['autor'] + kwargs.update({ + 'queryset': qs, + }) + return kwargs - if form.data['localizacao']: - kwargs['localizacao'] = form.data['localizacao'] + def get_context_data(self, **kwargs): + context = super(MateriaLegislativaPesquisaView, + self).get_context_data(**kwargs) - if form.data['em_tramitacao']: - kwargs['em_tramitacao'] = form.data['em_tramitacao'] + paginator = context['paginator'] + page_obj = context['page_obj'] - if form.data['situacao']: - kwargs['situacao'] = form.data['situacao'] + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages) - request.session['kwargs'] = kwargs + return context - return redirect('materia:pesquisar_materia_list') + def get(self, request, *args, **kwargs): + super(MateriaLegislativaPesquisaView, self).get(request) + + # Se a pesquisa estiver quebrando com a paginação + # Olhe esta função abaixo + # Provavelmente você criou um novo campo no Form/FilterSet + # Então a ordem da URL está diferente + data = self.filterset.data + if (data and data.get('tipo') is not None): + url = "&"+str(self.request.environ['QUERY_STRING']) + if url.startswith("&page"): + ponto_comeco = url.find('tipo=') - 1 + url = url[ponto_comeco:] + else: + url = '' + self.filterset.form.fields['o'].label = _('Ordenação') -class PesquisaMateriaListView(ListView): - template_name = 'materia/pesquisa_materia_list.html' - context_object_name = 'materias' - model = MateriaLegislativa - paginate_by = 10 + context = self.get_context_data(filter=self.filterset, + object_list=self.object_list, + filter_url=url, + numero_res=len(self.object_list) + ) - def get_queryset(self): - kwargs = self.request.session['kwargs'] - - materias = MateriaLegislativa.objects.all().order_by( - '-numero', '-ano') - - if 'apresentacao_inicial' in kwargs: - inicial = datetime.strptime( - kwargs['apresentacao_inicial'], - '%d/%m/%Y').strftime('%Y-%m-%d') - final = datetime.strptime( - kwargs['apresentacao_final'], - '%d/%m/%Y').strftime('%Y-%m-%d') - materias = materias.filter( - data_apresentacao__range=(inicial, final)) - - if 'publicacao_inicial' in kwargs: - inicial = datetime.strptime( - kwargs['publicacao_inicial'], - '%d/%m/%Y').strftime('%Y-%m-%d') - final = datetime.strptime( - kwargs['publicacao_final'], - '%d/%m/%Y').strftime('%Y-%m-%d') - materias = materias.filter( - data_publicacao__range=(inicial, final)) - - if 'tipo' in kwargs: - materias = materias.filter(tipo_id=kwargs['tipo']) - - if 'numero' in kwargs: - materias = materias.filter(numero=kwargs['numero']) - - if 'ano' in kwargs: - materias = materias.filter(ano=kwargs['ano']) - - if 'numero_protocolo' in kwargs: - materias = materias.filter(numero=kwargs['numero_protocolo']) - - if 'em_tramitacao' in kwargs: - materias = materias.filter(em_tramitacao=kwargs['em_tramitacao']) - - if 'local_origem_externa' in kwargs: - materias = materias.filter( - local_origem_externa=kwargs['local_origem_externa']) - - # autor - # localizao atual - # situacao - - return materias + return self.render_to_response(context) class ProposicaoView(CreateView): diff --git a/parlamentares/forms.py b/parlamentares/forms.py index 40d4c746e..3df9ae69e 100644 --- a/parlamentares/forms.py +++ b/parlamentares/forms.py @@ -4,24 +4,22 @@ from django import forms from django.db import transaction from django.forms import ModelForm from django.utils.translation import ugettext_lazy as _ -from floppyforms import ClearableFileInput import crispy_layout_mixin +import sapl from crispy_layout_mixin import form_actions from .models import Dependente, Filiacao, Legislatura, Mandato, Parlamentar -class ImageThumbnailFileInput(ClearableFileInput): - template_name = 'floppyforms/image_thumbnail.html' - - class ParlamentarForm(ModelForm): class Meta: model = Parlamentar exclude = [] - widgets = {'fotografia': ImageThumbnailFileInput} + widgets = {'fotografia': sapl.utils.ImageThumbnailFileInput, + 'biografia': forms.Textarea( + attrs={'id': 'texto-rico'})} class ParlamentarCreateForm(ParlamentarForm): diff --git a/parlamentares/models.py b/parlamentares/models.py index 0fb9f54ad..6bca49e95 100644 --- a/parlamentares/models.py +++ b/parlamentares/models.py @@ -239,8 +239,6 @@ class Parlamentar(models.Model): biografia = models.TextField( blank=True, verbose_name=_('Biografia')) # XXX Esse atribuito foi colocado aqui para não atrapalhar a migração - unidade_deliberativa = models.BooleanField( - default=False, verbose_name=_('Unidade Deliberativa')) fotografia = models.ImageField( blank=True, null=True, diff --git a/parlamentares/test_parlamentares_urls.py b/parlamentares/test_parlamentares_urls.py deleted file mode 100644 index c72c8c0be..000000000 --- a/parlamentares/test_parlamentares_urls.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.core.urlresolvers import reverse - - -def test_urls_materia(): - st = reverse('materia:pesquisar_materia_list') - assert st == '/materia/pesquisar-materia-list' diff --git a/protocoloadm/forms.py b/protocoloadm/forms.py index 4953007f5..21d4f99db 100644 --- a/protocoloadm/forms.py +++ b/protocoloadm/forms.py @@ -117,6 +117,11 @@ class ProtocoloFilterSet(django_filters.FilterSet): ('tipo_protocolo', 4), ('tipo_materia', 4)]) + row3 = crispy_layout_mixin.to_row( + [('tipo_documento', 4), + ('tipo_protocolo', 4), + ('tipo_materia', 4)]) + row3 = crispy_layout_mixin.to_row( [('interessado', 6), ('assunto_ementa', 6)]) diff --git a/protocoloadm/views.py b/protocoloadm/views.py index abdd4d747..980b7cdbe 100644 --- a/protocoloadm/views.py +++ b/protocoloadm/views.py @@ -245,6 +245,7 @@ class ComprovanteProtocoloView(TemplateView): autenticacao = _("** NULO **") if not protocolo.anulado: + # data is not i18n sensitive 'Y-m-d' is the right format. autenticacao = str(protocolo.tipo_processo) + \ protocolo.data.strftime("%Y/%m/%d") + \ str(protocolo.numero).zfill(6) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index dcb6a9fcb..10d6300cd 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -7,6 +7,7 @@ django-compressor==2.0 django-crispy-forms==1.6.0 python-decouple==3.0 django-extra-views==0.7.1 +django-filter==0.13.0 django-model-utils==2.4 django-sass-processor==0.3.4 django==1.9.5 diff --git a/sapl/settings.py b/sapl/settings.py index 1a1869b12..975b809e4 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -122,8 +122,8 @@ EMAIL_HOST_USER = config('EMAIL_HOST_USER', cast=str) EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', cast=str) EMAIL_PORT = config('EMAIL_PORT', cast=int) -MAX_DOC_UPLOAD_SIZE = 5*1024*1024 # 5MB -MAX_IMAGE_UPLOAD_SIZE = 2*1024*1024 # 2MB +MAX_DOC_UPLOAD_SIZE = 5 * 1024 * 1024 # 5MB +MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ @@ -188,4 +188,5 @@ SASS_PROCESSOR_INCLUDE_DIRS = (BOWER_COMPONENTS_ROOT.child( # see sapl.temp_suppress_crispy_form_warnings LOGGING = SUPRESS_CRISPY_FORM_WARNINGS_LOGGING +# suprime texto de ajuda default do django-filter FILTERS_HELP_TEXT_FILTER = False diff --git a/sapl/utils.py b/sapl/utils.py index c2a7c5f68..a94d7dfaf 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -6,6 +6,7 @@ from django.apps import apps from django.contrib import admin from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ +from floppyforms import ClearableFileInput autor_label = ''' @@ -33,6 +34,10 @@ autor_modal = ''' ''' +class ImageThumbnailFileInput(ClearableFileInput): + template_name = 'floppyforms/image_thumbnail.html' + + def register_all_models_in_admin(module_name): appname = module_name.split('.')[0] app = apps.get_app_config(appname) diff --git a/static/js/app.js b/static/js/app.js index 4507b64b6..a5af75510 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -105,9 +105,17 @@ function autorModal() { id = res.val(); nome = res.text(); - $("#id_autor").val(id); $("#nome_autor").text(nome); + // MateriaLegislativa pesquisa Autor via a tabela Autoria + if ($('#id_autoria__autor').length) { + $('#id_autoria__autor').val(id); + } + // Protocolo pesquisa a própria tabela de Autor + if ($('#id_autor').length) { + $("#id_autor").val(id); + } + dialog.dialog( "close" ); }); }); @@ -119,5 +127,5 @@ $(document).ready(function(){ refreshDatePicker(); refreshMask(); autorModal(); - initTinymce("biografia-parlamentar,casa-informacoes"); + initTinymce("texto-rico"); }); diff --git a/templates/materia/materialegislativa_filter.html b/templates/materia/materialegislativa_filter.html new file mode 100644 index 000000000..bb3f187b8 --- /dev/null +++ b/templates/materia/materialegislativa_filter.html @@ -0,0 +1,48 @@ +{% extends "materia/materialegislativa_detail.html" %} +{% load i18n %} +{% load crispy_forms_tags %} +{% block actions %}{% endblock %} + +{% block sections_nav %} {% endblock %} + +{% block detail_content %} +

Pesquisar Matéria

+

+ + + {% crispy filter.form %} + +

+ + + + + {% if page_obj|length %} + {% if numero_res > 1 %} +

Pesquisa concluída com sucesso! Foram encontradas {{numero_res}} matérias.

+ {% elif numero_res == 1 %} +

Pesquisa concluída com sucesso! Foi encontrada {{numero_res}} matéria.

+ {% endif %} + + {% for m in page_obj %} + + + + {% endfor %} + {% else %} + + + {% endif %} + +

Resultados

+ {{m.tipo.sigla}} {{m.numero}}/{{m.ano}} - {{m.tipo}}
+ {{ m.ementa|safe }}
+ Localização Atual: {{m.tramitacao_set.last.unidade_tramitacao_destino|default_if_none:"Não Informada"}}
+

+
+

Nenhuma matéria encontrada com essas especificações

+
+ +{% include "paginacao.html" %} + +{% endblock detail_content %} \ No newline at end of file diff --git a/templates/materia/pesquisa_materia.html b/templates/materia/pesquisa_materia.html deleted file mode 100644 index 56f5cfa55..000000000 --- a/templates/materia/pesquisa_materia.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "materia/materialegislativa_detail.html" %} -{% load i18n %} -{% load crispy_forms_tags %} -{% block actions %}{% endblock %} -{% block sections_nav %}{% endblock %} -{% block detail_content %} - {% crispy form %} -{% endblock %} diff --git a/templates/materia/pesquisa_materia_list.html b/templates/materia/pesquisa_materia_list.html deleted file mode 100644 index 83a17712a..000000000 --- a/templates/materia/pesquisa_materia_list.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "materia/materialegislativa_detail.html" %} -{% load i18n %} -{% load crispy_forms_tags %} -{% block actions %}{% endblock %} -{% block sections_nav %} {% endblock %} -{% block detail_content %} -{% if materias %} - - - - - {% for m in materias %} - - - {% endfor %} -

Resultados

- {{m.tipo.sigla}} {{m.numero}}/{{m.ano}} - {{m.tipo}}
- {{ m.ementa|safe }}
- Localização Atual: {{m.tramitacao_set.last.unidade_tramitacao_destino|default_if_none:"Não Informada"}}
-

-
-{% include "paginacao.html" %} -{% else %} -

Nenhum Registro recuperado

-{% endif %} -{% endblock detail_content %} diff --git a/templates/materia/proposicao/proposicao_list.html b/templates/materia/proposicao/proposicao_list.html index afbad4ade..566dc8566 100644 --- a/templates/materia/proposicao/proposicao_list.html +++ b/templates/materia/proposicao/proposicao_list.html @@ -3,16 +3,16 @@ {% load crispy_forms_tags %} {% block actions %}{% endblock %} - +{% endblock %} {% block detail_content %} -
+

Proposições

+ -

Proposições

+