diff --git a/sapl/base/forms.py b/sapl/base/forms.py index 2ccde243a..99d6842ad 100644 --- a/sapl/base/forms.py +++ b/sapl/base/forms.py @@ -1583,7 +1583,8 @@ class ConfiguracoesAppForm(ModelForm): 'tramitacao_documento', 'google_recaptcha_site_key', 'google_recaptcha_secret_key', - 'sapl_as_sapn'] + 'sapl_as_sapn', + 'identificacao_de_documentos'] def __init__(self, *args, **kwargs): super(ConfiguracoesAppForm, self).__init__(*args, **kwargs) diff --git a/sapl/base/migrations/0051_auto_20220814_2138.py b/sapl/base/migrations/0051_auto_20220814_2138.py new file mode 100644 index 000000000..39f98c00f --- /dev/null +++ b/sapl/base/migrations/0051_auto_20220814_2138.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.28 on 2022-08-15 00:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0050_metadata'), + ] + + operations = [ + migrations.AddField( + model_name='appconfig', + name='identificacao_de_documentos', + field=models.CharField(default='{sigla} Nº {numero}/{ano}{-}{complemento} - {nome}', help_text='\n Como mostrar a identificação dos documentos administrativos?\n Você pode usar um conjunto de combinações que pretender.\n Ao fazer sua edição, será mostrado logo abaixo o último documento cadastrado, como exemplo de resultado de sua edição.\n Em caso de erro, nenhum documento será mostrado e aparecerá apenas o formato padrão mínimo, que é este: "{sigla} Nº {numero}/{ano}{-}{complemento} - {nome}".\n Muito importante, use as chaves "{}", sem elas, você estará inserindo um texto qualquer e não o valor de um campo.\n Você pode combinar as seguintes campos: {sigla} {nome} {numero} {ano} {complemento} {assunto}\n Ainda pode ser usado {/}, {-}, {.} se você quiser que uma barra, traço, ou ponto\n seja adicionado apenas se o próximo campo que será usado tenha algum conteúdo\n (não use dois destes destes condicionais em sequência, somente o último será considerado).\n ', max_length=254, verbose_name='Formato da identificação dos documentos'), + ), + migrations.AlterField( + model_name='appconfig', + name='protocolo_manual', + field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Permitir informe manual de data e hora de protocolo?'), + ), + ] diff --git a/sapl/base/models.py b/sapl/base/models.py index c27db2748..88f315aba 100644 --- a/sapl/base/models.py +++ b/sapl/base/models.py @@ -1,6 +1,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields.jsonb import JSONField +from django.core.cache import cache from django.core.serializers.json import DjangoJSONEncoder from django.db import models from django.db.models.deletion import CASCADE @@ -93,63 +94,124 @@ class AppConfig(models.Model): ('N', _('Nunca Protocolar ao incorporar uma proposição')), ) - documentos_administrativos = models.CharField( - max_length=1, - verbose_name=_('Visibilidade dos Documentos Administrativos'), - choices=TIPO_DOCUMENTO_ADMINISTRATIVO, default='O') + # MANTENHA A SEQUÊNCIA EQUIVALENTE COM /sapl/templates/base/layout.yaml + # AppConfig: - estatisticas_acesso_normas = models.CharField( + # CONFIGURAÇÕES GERAIS + # Linha 1 ------------ + esfera_federacao = models.CharField( max_length=1, - verbose_name=_('Estatísticas de acesso a normas'), - choices=RELATORIO_ATOS_ACESSADOS, default='N') + blank=True, + default="", + verbose_name=_('Esfera Federação'), + choices=ESFERA_FEDERACAO_CHOICES) + sapl_as_sapn = models.BooleanField( + verbose_name=_( + 'Utilizar SAPL como SAPN?'), + choices=YES_NO_CHOICES, default=False) - sequencia_numeracao_proposicao = models.CharField( - max_length=1, - verbose_name=_('Sequência de numeração de proposições'), - choices=SEQUENCIA_NUMERACAO_PROPOSICAO, default='A') + # MÓDULO PARLAMENTARES + # MÓDULO MESA DIRETORA + + # MÓDULO COMISSÕES + + # MÓDULO BANCADAS PARLAMENTARES + + # MÓDULO DOCUMENTOS ADMINISTRATIVOS + # Linha 1 ------------------------- + documentos_administrativos = models.CharField( + max_length=1, + verbose_name=_('Visibilidade dos Documentos Administrativos'), + choices=TIPO_DOCUMENTO_ADMINISTRATIVO, default='O') + tramitacao_documento = models.BooleanField( + verbose_name=_( + 'Tramitar documentos anexados junto com os documentos principais?'), + choices=YES_NO_CHOICES, default=True) + # Linha 2 ------------------------- + protocolo_manual = models.BooleanField( + verbose_name=_('Permitir informe manual de data e hora de protocolo?'), + choices=YES_NO_CHOICES, default=False) sequencia_numeracao_protocolo = models.CharField( max_length=1, verbose_name=_('Sequência de numeração de protocolos'), choices=SEQUENCIA_NUMERACAO_PROTOCOLO, default='A') - inicio_numeracao_protocolo = models.PositiveIntegerField( verbose_name=_('Início da numeração de protocolo'), default=1 ) + # Linha 3 ------------------------- + identificacao_de_documentos = models.CharField( + max_length=254, + verbose_name=_('Formato da identificação dos documentos'), + default='{sigla} Nº {numero}/{ano}{-}{complemento} - {nome}', + help_text=""" + Como mostrar a identificação dos documentos administrativos? + Você pode usar um conjunto de combinações que pretender. + Ao fazer sua edição, será mostrado logo abaixo o último documento cadastrado, como exemplo de resultado de sua edição. + Em caso de erro, nenhum documento será mostrado e aparecerá apenas o formato padrão mínimo, que é este: "{sigla} Nº {numero}/{ano}{-}{complemento} - {nome}". + Muito importante, use as chaves "{}", sem elas, você estará inserindo um texto qualquer e não o valor de um campo. + Você pode combinar as seguintes campos: {sigla} {nome} {numero} {ano} {complemento} {assunto} + Ainda pode ser usado {/}, {-}, {.} se você quiser que uma barra, traço, ou ponto + seja adicionado apenas se o próximo campo que será usado tenha algum conteúdo + (não use dois destes destes condicionais em sequência, somente o último será considerado). + """ + ) - esfera_federacao = models.CharField( + # MÓDULO PROPOSIÇÕES + # Linha 1 ---------- + sequencia_numeracao_proposicao = models.CharField( max_length=1, - blank=True, - default="", - verbose_name=_('Esfera Federação'), - choices=ESFERA_FEDERACAO_CHOICES) + verbose_name=_('Sequência de numeração de proposições'), + choices=SEQUENCIA_NUMERACAO_PROPOSICAO, default='A') + receber_recibo_proposicao = models.BooleanField( + verbose_name=_('Protocolar proposição somente com recibo?'), + choices=YES_NO_CHOICES, default=True) + proposicao_incorporacao_obrigatoria = models.CharField( + verbose_name=_('Regra de incorporação de proposições e protocolo'), + max_length=1, choices=POLITICA_PROTOCOLO_CHOICES, default='O') + escolher_numero_materia_proposicao = models.BooleanField( + verbose_name=_( + 'Indicar número da matéria a ser gerada na proposição?'), + choices=YES_NO_CHOICES, default=False) - # TODO: a ser implementado na versão 3.2 - # painel_aberto = models.BooleanField( - # verbose_name=_('Painel aberto para usuário anônimo'), - # choices=YES_NO_CHOICES, default=False) + # MÓDULO MATÉRIA LEGISLATIVA + # Linha 1 ------------------ + tramitacao_origem_fixa = models.BooleanField( + verbose_name=_( + 'Fixar origem de novas tramitações como sendo a tramitação de destino da última tramitação?'), + choices=YES_NO_CHOICES, + default=True, + help_text=_('Ao utilizar a opção NÂO, você compreende que os controles ' + 'de origem e destino das tramitações são anulados, ' + 'podendo seu operador registrar quaisquer origem e ' + 'destino para as tramitações. Se você colocar Não, ' + 'fizer tramitações aleatórias e voltar para SIM, ' + 'o destino da tramitação mais recente será utilizado ' + 'para a origem de uma nova inserção!')) + tramitacao_materia = models.BooleanField( + verbose_name=_( + 'Tramitar matérias anexadas junto com as matérias principais?'), + choices=YES_NO_CHOICES, default=True) + # MÓDULO NORMAS JURÍDICAS + # MÓDULO TEXTOS ARTICULADOS + # Linha 1 ----------------- texto_articulado_proposicao = models.BooleanField( verbose_name=_('Usar Textos Articulados para Proposições'), choices=YES_NO_CHOICES, default=False) - texto_articulado_materia = models.BooleanField( verbose_name=_('Usar Textos Articulados para Matérias'), choices=YES_NO_CHOICES, default=False) - texto_articulado_norma = models.BooleanField( verbose_name=_('Usar Textos Articulados para Normas'), choices=YES_NO_CHOICES, default=True) - proposicao_incorporacao_obrigatoria = models.CharField( - verbose_name=_('Regra de incorporação de proposições e protocolo'), - max_length=1, choices=POLITICA_PROTOCOLO_CHOICES, default='O') - + # MÓDULO SESSÃO PLENÁRIA assinatura_ata = models.CharField( verbose_name=_('Quem deve assinar a ata'), max_length=1, choices=ASSINATURA_ATA_CHOICES, default='T') - + # MÓDULO PAINEL cronometro_discurso = models.DurationField( verbose_name=_('Cronômetro do Discurso'), blank=True, @@ -174,41 +236,20 @@ class AppConfig(models.Model): default=False, verbose_name=_('Mostrar brasão da Casa no painel?')) - receber_recibo_proposicao = models.BooleanField( - verbose_name=_('Protocolar proposição somente com recibo?'), - choices=YES_NO_CHOICES, default=True) - - protocolo_manual = models.BooleanField( - verbose_name=_('Informar data e hora de protocolo?'), - choices=YES_NO_CHOICES, default=False) - - escolher_numero_materia_proposicao = models.BooleanField( - verbose_name=_( - 'Indicar número da matéria a ser gerada na proposição?'), - choices=YES_NO_CHOICES, default=False) + # MÓDULO ESTATÍSTICAS DE ACESSO + estatisticas_acesso_normas = models.CharField( + max_length=1, + verbose_name=_('Estatísticas de acesso a normas'), + choices=RELATORIO_ATOS_ACESSADOS, default='N') - tramitacao_origem_fixa = models.BooleanField( - verbose_name=_( - 'Fixar origem de novas tramitações como sendo a tramitação de destino da última tramitação?'), - choices=YES_NO_CHOICES, - default=True, - help_text=_('Ao utilizar a opção NÂO, você compreende que os controles ' - 'de origem e destino das tramitações são anulados, ' - 'podendo seu operador registrar quaisquer origem e ' - 'destino para as tramitações. Se você colocar Não, ' - 'fizer tramitações aleatórias e voltar para SIM, ' - 'o destino da tramitação mais recente será utilizado ' - 'para a origem de uma nova inserção!')) + # MÓDULO SEGURANÇA - tramitacao_materia = models.BooleanField( - verbose_name=_( - 'Tramitar matérias anexadas junto com as matérias principais?'), - choices=YES_NO_CHOICES, default=True) + # MÓDULO LEXML - tramitacao_documento = models.BooleanField( - verbose_name=_( - 'Tramitar documentos anexados junto com os documentos principais?'), - choices=YES_NO_CHOICES, default=True) + # TODO: a ser implementado na versão 3.2 + # painel_aberto = models.BooleanField( + # verbose_name=_('Painel aberto para usuário anônimo'), + # choices=YES_NO_CHOICES, default=False) google_recaptcha_site_key = models.CharField( verbose_name=_('Chave pública gerada pelo Google Recaptcha'), @@ -217,11 +258,6 @@ class AppConfig(models.Model): verbose_name=_('Chave privada gerada pelo Google Recaptcha'), max_length=256, default='') - sapl_as_sapn = models.BooleanField( - verbose_name=_( - 'Utilizar SAPL como SAPN?'), - choices=YES_NO_CHOICES, default=False) - class Meta: verbose_name = _('Configurações da Aplicação') verbose_name_plural = _('Configurações da Aplicação') @@ -231,15 +267,32 @@ class AppConfig(models.Model): ) ordering = ('-id',) + def save(self, force_insert=False, force_update=False, using=None, + update_fields=None): + fields = self._meta.get_fields() + for f in fields: + if f.name != 'id' and not cache.get(f'sapl_{f.name}') is None: + cache.set(f'sapl_{f.name}', getattr(self, f.name), 600) + + return models.Model.save(self, force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) + @classmethod def attr(cls, attr): + value = cache.get(f'sapl_{attr}') + if not value is None: + return value + print(f'entrou aqui para {attr}') + config = AppConfig.objects.first() if not config: config = AppConfig() config.save() - return getattr(config, attr) + value = getattr(config, attr) + cache.set(f'sapl_{attr}', value, 600) + + return value def __str__(self): return _('Configurações da Aplicação - %(id)s') % { diff --git a/sapl/base/views.py b/sapl/base/views.py index f1385e3a2..05a6c8f49 100644 --- a/sapl/base/views.py +++ b/sapl/base/views.py @@ -4,7 +4,9 @@ import datetime import itertools import logging import os +import re +from django.apps.registry import apps from django.contrib import messages from django.contrib.auth import get_user_model, views from django.contrib.auth.mixins import PermissionRequiredMixin @@ -32,8 +34,8 @@ from django.views.generic.base import RedirectView, TemplateView from django_filters.views import FilterView from haystack.query import SearchQuerySet from haystack.views import SearchView - from ratelimit.decorators import ratelimit + from sapl import settings from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica from sapl.base.forms import (AutorForm, TipoAutorForm, AutorFilterSet, RecuperarSenhaForm, @@ -1893,6 +1895,7 @@ class UserCrud(Crud): list_field_names = [ 'usuario', 'groups', 'is_active' ] + def openapi_url(self): return '' @@ -1927,7 +1930,6 @@ class UserCrud(Crud): class DetailView(Crud.DetailView): layout_key = 'UserDetail' - def hook_usuario(self, obj): return 'Usuário', '{}
{}'.format( obj.get_full_name() or '...', @@ -2100,10 +2102,45 @@ class AppConfigCrud(CrudAux): kwargs={'pk': app_config.pk})) class UpdateView(CrudAux.UpdateView): - - template_name = 'base/AppConfig.html' form_class = ConfiguracoesAppForm + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['title'] = self.model._meta.verbose_name + return context + + def get(self, request, *args, **kwargs): + if 'jsidd' in request.GET: + return self.json_simular_identificacao_de_documentos(request, *args, **kwargs) + + return super().get(request, *args, **kwargs) + + def json_simular_identificacao_de_documentos(self, request, *args, **kwargs): + + DocumentoAdministrativo = apps.get_model( + 'protocoloadm', + 'DocumentoAdministrativo' + ) + + d = DocumentoAdministrativo.objects.order_by('-id').first() + + jsidd = request.GET.get('jsidd', '') + values = { + '{sigla}': d.tipo.sigla if d else 'OF', + '{nome}': d.tipo.descricao if d else 'Ofício', + '{numero}': f'{d.numero:0>3}' if d else '001', + '{ano}': f'{d.ano}' if d else str(timezone.now().year), + '{complemento}': d.complemento if d else 'GAB', + '{assunto}': d.assunto if d else 'Simulação de Identificação de Documentos' + } + + result = DocumentoAdministrativo.mask_to_str(values, jsidd) + + return JsonResponse({ + 'jsidd': result[0], + 'error': list(result[1]) + }) + def form_valid(self, form): numeracao = AppConfig.objects.last().sequencia_numeracao_protocolo numeracao_antiga = AppConfig.objects.last().inicio_numeracao_protocolo diff --git a/sapl/norma/models.py b/sapl/norma/models.py index a24c7ac86..2fbd96ccf 100644 --- a/sapl/norma/models.py +++ b/sapl/norma/models.py @@ -151,7 +151,9 @@ class NormaJuridica(models.Model): verbose_name=_('Tipo da Norma Jurídica')) materia = models.ForeignKey( MateriaLegislativa, blank=True, null=True, - on_delete=models.PROTECT, verbose_name=_('Matéria')) + on_delete=models.PROTECT, + verbose_name=_('Matéria'), + related_name='normajuridica_set') orgao = models.ForeignKey( Orgao, blank=True, null=True, on_delete=models.PROTECT, verbose_name=_('Órgão')) diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py index 744d30e5e..58223f21c 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -1139,9 +1139,10 @@ class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm): tipo = TipoDocumentoAdministrativo.objects.get( id=tipo_documento) raise ValidationError( - _('{}/{} ({}) já existente!'.format(numero_documento, - ano_documento, - tipo))) + _('{}/{} ({}) já existente! ' + 'Você diferenciar preenchendo o campo complemento'.format(numero_documento, + ano_documento, + tipo))) # campos opcionais, mas que se informados devem ser válidos if numero_protocolo and ano_protocolo: diff --git a/sapl/protocoloadm/models.py b/sapl/protocoloadm/models.py index 82f899465..883ca4274 100644 --- a/sapl/protocoloadm/models.py +++ b/sapl/protocoloadm/models.py @@ -1,10 +1,13 @@ +import re + from django.db import models from django.utils import timezone +from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ from model_utils import Choices import reversion -from sapl.base.models import Autor +from sapl.base.models import Autor, AppConfig as SaplAppConfig from sapl.materia.models import TipoMateriaLegislativa, UnidadeTramitacao,\ MateriaLegislativa from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, texto_upload_path, @@ -250,19 +253,67 @@ class DocumentoAdministrativo(models.Model): verbose_name_plural = _('Documentos Administrativos') ordering = ('id',) - def __str__(self): - return _('%(tipo)s - %(assunto)s') % { - 'tipo': self.tipo, 'assunto': self.assunto + @classmethod + def mask_to_str(cls, values, mask): + erro = set() + pattern = '({[^{}]+}|{[ /.-]*})' + campos_escolhidos = re.findall(pattern, mask) + campos_permitidos = { + '{.}', '{/}', '{-}', + '{sigla}', + '{nome}', + '{numero}', + '{ano}', + '{complemento}', + '{assunto}', + } + condicionais = { + '{.}': '.', + '{/}': '/', + '{-}': '-', } - @property - def epigrafe(self): - return _('%(tipo)s - %(numero)s/%(ano)s') % { - 'tipo': self.tipo, - 'numero': self.numero, - 'ano': self.ano + erro = set(campos_escolhidos) - campos_permitidos + + if erro: + mask = '{sigla} Nº {numero}/{ano}{-}{complemento} - {nome}' + campos_escolhidos = re.findall(pattern, mask) + + for i, k in enumerate(campos_escolhidos): + if k in values.keys(): + if i > 0 and campos_escolhidos[i - 1] in condicionais: + mask = mask.replace( + campos_escolhidos[i - 1], + condicionais[campos_escolhidos[i - 1]]if values[k] else '', 1) + mask = mask.replace(k, values[k], 1) + elif k in condicionais: + if i > 0 and campos_escolhidos[i - 1] in condicionais: + mask = mask.replace( + campos_escolhidos[i - 1], + '', 1) + if i + 1 == len(campos_escolhidos): + mask = mask.replace(k, '', 1) + + return mask, erro + + @cached_property + def _identificacao_de_documento(self): + mask = SaplAppConfig.attr('identificacao_de_documentos') + + values = { + '{sigla}': self.tipo.sigla, + '{nome}': self.tipo.descricao, + '{numero}': f'{self.numero:0>3}', + '{ano}': f'{self.ano}', + '{complemento}': self.complemento, + '{assunto}': self.assunto } + return DocumentoAdministrativo.mask_to_str(values, mask)[0] + + def __str__(self): + return self._identificacao_de_documento + def delete(self, using=None, keep_parents=False): texto_integral = self.texto_integral result = super().delete(using=using, keep_parents=keep_parents) @@ -487,11 +538,6 @@ class VinculoDocAdminMateria(models.Model): def __str__(self): return f'Vinculo: {self.documento} - {self.materia}' - return _('Vinculo %(documento)s // %(materia)s') % { - 'documento': self.documento.epigrafe, - 'materia': self.materia - } - @reversion.register() class AcompanhamentoDocumento(models.Model): diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py index 869c6d240..2b1346f7b 100755 --- a/sapl/protocoloadm/views.py +++ b/sapl/protocoloadm/views.py @@ -1783,7 +1783,7 @@ class VinculoDocAdminMateriaCrud(MasterDetailCrud): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['title'] = self.object.documento.epigrafe + context['title'] = self.object.documento return context def get_initial(self): diff --git a/sapl/templates/base/AppConfig.html b/sapl/templates/base/AppConfig.html deleted file mode 100644 index 6865a8500..000000000 --- a/sapl/templates/base/AppConfig.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "base.html" %} -{% load i18n crispy_forms_tags menus%} - -{% block base_content %} -{% crispy form %} -{% endblock base_content %} diff --git a/sapl/templates/base/appconfig_form.html b/sapl/templates/base/appconfig_form.html new file mode 100644 index 000000000..2f152f06e --- /dev/null +++ b/sapl/templates/base/appconfig_form.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} +{% load i18n crispy_forms_tags menus%} + +{% block base_content %} +{% crispy form %} +{% endblock base_content %} + +{% block extra_js %} + +{% endblock %} diff --git a/sapl/templates/base/layouts.yaml b/sapl/templates/base/layouts.yaml index 69c06a1c4..e9029810d 100644 --- a/sapl/templates/base/layouts.yaml +++ b/sapl/templates/base/layouts.yaml @@ -20,40 +20,29 @@ UserDetail: AppConfig: {% trans 'Configurações Gerais' %}: - - esfera_federacao documentos_administrativos sapl_as_sapn + - esfera_federacao sapl_as_sapn #{% trans 'Módulo Parlamentares' %}: - #{% trans 'Módulo Mesa Diretora' %}: - #{% trans 'Módulo Comissões' %}: - #{% trans 'Módulo Bancadas Parlamentares' %}: - # {% trans 'Módulo Normas Jurídicas' %}: + {% trans 'Módulo Administrativo' %}: + - documentos_administrativos tramitacao_documento + - protocolo_manual sequencia_numeracao_protocolo inicio_numeracao_protocolo + - identificacao_de_documentos {% trans 'Módulo Proposições' %}: - - sequencia_numeracao_proposicao sequencia_numeracao_protocolo inicio_numeracao_protocolo - - protocolo_manual receber_recibo_proposicao - - proposicao_incorporacao_obrigatoria escolher_numero_materia_proposicao + - sequencia_numeracao_proposicao receber_recibo_proposicao proposicao_incorporacao_obrigatoria escolher_numero_materia_proposicao {% trans 'Módulo Matéria Legislativa' %}: - - tramitacao_origem_fixa tramitacao_materia tramitacao_documento + - tramitacao_origem_fixa:7 tramitacao_materia + # {% trans 'Módulo Normas Jurídicas' %}: {% trans 'Módulo Textos Articulados' %}: - texto_articulado_proposicao texto_articulado_materia texto_articulado_norma - #{% trans 'Módulo Sessão Plenária' %}: - - #{% trans 'Módulo LexML' %}: - - #{% trans 'Módulo Administrativo' %}: - - - {% trans 'Estatísticas de acesso' %}: - - estatisticas_acesso_normas - - {% trans 'Assinaturas' %}: + {% trans 'Módulo Sessão Plenária' %}: - assinatura_ata {% trans 'Módulo Painel' %}: @@ -61,9 +50,14 @@ AppConfig: - cronometro_ordem cronometro_consideracoes - mostrar_brasao_painel + {% trans 'Estatísticas de acesso' %}: + - estatisticas_acesso_normas + {% trans 'Segurança' %}: - google_recaptcha_site_key google_recaptcha_secret_key + #{% trans 'Módulo LexML' %}: + TipoAutor: {% trans 'Tipo Autor' %}: - descricao diff --git a/sapl/templates/materia/layouts.yaml b/sapl/templates/materia/layouts.yaml index 142dcdf91..6ab3dbf2e 100644 --- a/sapl/templates/materia/layouts.yaml +++ b/sapl/templates/materia/layouts.yaml @@ -29,6 +29,7 @@ MateriaLegislativa: - data_apresentacao numero_protocolo tipo_apresentacao - tipo_autor autor - texto_original + {% trans 'Outras Informações' %}: - apelido dias_prazo polemica - objeto regime_tramitacao em_tramitacao diff --git a/sapl/templates/materia/materialegislativa_detail.html b/sapl/templates/materia/materialegislativa_detail.html index 3b736318f..032ccb3c2 100644 --- a/sapl/templates/materia/materialegislativa_detail.html +++ b/sapl/templates/materia/materialegislativa_detail.html @@ -19,33 +19,43 @@ {% block detail_content %} {{ block.super }} {% if object.numero_protocolo %} - Protocolo: {{ object.numero_protocolo }}/{{ object.ano }}, - Data Protocolo: + Protocolo: {{ object.numero_protocolo }}/{{ object.ano }}, + Data Protocolo: {{ object.data_entrada_protocolo|localtime|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ object.data_entrada_protocolo|localtime|date:"G:i:s" }}
{% endif %} {% if object.registrovotacao_set.exists %} - Data Votação: - {% for rv in object.registrovotacao_set.all %} - {% if rv.ordem %} - - {{ rv.ordem.sessao_plenaria.data_inicio }} - - {% elif rv.expediente %} - - {{ rv.expediente.sessao_plenaria.data_inicio }} - - {% endif %} -
- {% endfor %} + Data Votação: + {% for rv in object.registrovotacao_set.all %} + {% if rv.ordem %} + + {{ rv.ordem.sessao_plenaria.data_inicio }} + + {% elif rv.expediente %} + + {{ rv.expediente.sessao_plenaria.data_inicio }} + + {% endif %} +
+ {% endfor %} {% endif %} + {% if object.normajuridica_set.last %} -

  Norma Jurídica Relacionada

-
-    - {{ object.normajuridica_set.last }} -
+
+
+
+
+

Norma Jurídica Relacionada

+ +
+
+
{% endif %} {% if object.docadmvinculados.all.exists %} @@ -55,13 +65,13 @@

Documentos Administrativos Públicos Vinculados a Matéria

-
+
{% for vinculodocadmmateria in object.documentoadministrativo_vinculado_set.all %} {% if not vinculodocadmmateria.documento.restrito or vinculodocadmmateria.documento.restrito and not user.is_anonymous %} Data Anexação: {{vinculodocadmmateria.data_anexacao}} {% if vinculodocadmmateria.data_desanexacao %} - {{vinculodocadmmateria.data_desanexacao}}{% endif %}
Documento: - {{ vinculodocadmmateria.documento.epigrafe }} + {{ vinculodocadmmateria.documento }}
{{ vinculodocadmmateria.documento.assunto}} {% if vinculodocadmmateria.documento.restrito %} diff --git a/sapl/templates/protocoloadm/documentoadministrativo_filter.html b/sapl/templates/protocoloadm/documentoadministrativo_filter.html index cbd3274fd..4fd0b65b7 100644 --- a/sapl/templates/protocoloadm/documentoadministrativo_filter.html +++ b/sapl/templates/protocoloadm/documentoadministrativo_filter.html @@ -37,7 +37,7 @@ {% if request.user.is_anonymous and not d.restrito or not request.user.is_anonymous%} - {{d.tipo.sigla}} {{d.numero}}{%if d.complemento %}-{{d.complemento}}{% endif %}/{{d.ano}} - {{d.tipo}}
+ {{d}}
Interessado: {{ d.interessado|default_if_none:"Não informado"}}
Assunto: {{ d.assunto|safe }} @@ -108,7 +108,7 @@ $("#id_ano").removeClass("select-error"); document.getElementById("error-ano").remove(); } - + } } diff --git a/sapl/templates/protocoloadm/layouts.yaml b/sapl/templates/protocoloadm/layouts.yaml index c223f686a..cbfe3210f 100644 --- a/sapl/templates/protocoloadm/layouts.yaml +++ b/sapl/templates/protocoloadm/layouts.yaml @@ -9,7 +9,7 @@ DocumentoAdministrativo: - numero complemento ano - data protocolo - assunto - - interessado autor tramitacao + - interessado autor tramitacao:2 - texto_integral|urlize - documento_anexado_set__documento_principal|m2m_urlize_for_detail - documento_principal_set__documento_anexado|m2m_urlize_for_detail diff --git a/sapl/utils.py b/sapl/utils.py index 045acda62..928e7fe7c 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -1,6 +1,6 @@ from functools import wraps import hashlib -from itertools import groupby +from itertools import groupby, chain import logging from operator import itemgetter import os @@ -26,6 +26,7 @@ from django.core.files.uploadedfile import UploadedFile from django.core.mail import get_connection from django.db import models from django.db.models import Q +from django.db.models.fields.related import ForeignKey from django.forms import BaseForm from django.forms.widgets import SplitDateTimeWidget from django.utils import six, timezone