diff --git a/sapl/crud/base.py b/sapl/crud/base.py index 3ec86b160..5306076a6 100644 --- a/sapl/crud/base.py +++ b/sapl/crud/base.py @@ -9,11 +9,11 @@ from django.conf.urls import url from django.contrib import messages from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ObjectDoesNotExist -from django.urls import reverse from django.db import models from django.db.models.fields.related import ForeignKey, ManyToManyField from django.http.response import Http404 from django.shortcuts import redirect +from django.urls import reverse from django.utils.decorators import classonlymethod from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ @@ -29,6 +29,7 @@ from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL, RP_LIST) from sapl.utils import normalize + logger = logging.getLogger(settings.BASE_DIR.name) ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \ @@ -79,6 +80,7 @@ def make_pagination(index, num_pages): head = from_to(1, PAGINATION_LENGTH - len(tail) - 1) return head + [None] + tail + """ variáveis do crud: help_topic @@ -416,16 +418,18 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): for f in fn: if not f: continue - f = m._meta.get_field(f) - if hasattr(f, 'related_model') and f.related_model: - m = f.related_model - if f: - hook = 'hook_header_{}'.format(''.join(fn)) - if hasattr(self, hook): - header = getattr(self, hook)() - s.append(header) - else: - s.append(force_text(f.verbose_name)) + try: + f = m._meta.get_field(f) + if hasattr(f, 'related_model') and f.related_model: + m = f.related_model + except: + f = None + hook = 'hook_header_{}'.format(''.join(fn)) + if hasattr(self, hook): + header = getattr(self, hook)() + s.append(header) + elif f: + s.append(force_text(f.verbose_name)) s = ' / '.join(s) r.append(s) @@ -607,7 +611,8 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): # print(ordering) except Exception as e: - logger.warning(_(f"ERRO: construção da tupla de ordenação. {e}")) + logger.warning( + _(f"ERRO: construção da tupla de ordenação. {e}")) # print(queryset.query) if not self.request.user.is_authenticated: @@ -627,7 +632,7 @@ class AuditLogMixin(object): # Classe deve implementar um get_object(), i.e., deve ser uma View deleted_object = self.get_object() try: - return super(AuditLogMixin, self).delete(request, args, kwargs) + return super(AuditLogMixin, self).delete(request, args, kwargs) finally: post_delete_signal.send(sender=None, instance=deleted_object, diff --git a/sapl/norma/models.py b/sapl/norma/models.py index 099ae6ade..ee8522746 100644 --- a/sapl/norma/models.py +++ b/sapl/norma/models.py @@ -1,8 +1,8 @@ from django.contrib.contenttypes.fields import GenericRelation from django.db import models from django.template import defaultfilters -from django.utils.translation import ugettext_lazy as _ from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ from model_utils import Choices import reversion @@ -10,7 +10,7 @@ from sapl.base.models import Autor from sapl.compilacao.models import TextoArticulado from sapl.materia.models import MateriaLegislativa from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, - restringe_tipos_de_arquivo_txt, + restringe_tipos_de_arquivo_txt, texto_upload_path, get_settings_auth_user_model, OverwriteStorage) @@ -70,8 +70,66 @@ def norma_upload_path(instance, filename): return texto_upload_path(instance, filename, subpath=instance.ano) +class NormaJuridicaManager(models.Manager): + + use_for_related_fields = True + + def normas_sem_textos_articulados(self): + qs = self.get_queryset() + qs = qs.filter(texto_articulado__isnull=True) + return qs + + def normas_com_textos_articulados_publicados(self): + qs = self.get_queryset() + qs = qs.filter( + texto_articulado__editable_only_by_owners=False, + texto_articulado__privacidade=0, + texto_articulado__isnull=False + ) + + return qs + + def normas_com_textos_articulados_pendentes(self): + qs = self.get_queryset() + qs = qs.filter( + texto_articulado__editable_only_by_owners=False) + + q = models.Q( + texto_articulado__privacidade=0 + ) | models.Q( + texto_articulado__isnull=True + ) + qs = qs.exclude(q) + + for n in qs: + ta = n.texto_articulado.first() + count = ta.dispositivos_set.count() + if count == 1: + count = 0 + elif count == 2: + d = ta.dispositivos_set.last() + if d.auto_inserido or not d.texto or d.texto == n.ementa: + count = 0 + elif count == 3: + ds = ta.dispositivos_set.all() + if ds[1].auto_inserido and \ + not d[2].dispositivo_pai and\ + d[2].tipo_dispositivo.dispositivo_de_articulacao: + count = 0 + + if not count: + ta.dispositivos_set.filter( + dispositivo_pai__isnull=False).delete() + ta.delete() + + return qs + + @reversion.register() class NormaJuridica(models.Model): + + objects = NormaJuridicaManager() + ESFERA_FEDERACAO_CHOICES = Choices( ('M', 'municipal', _('Municipal')), ('E', 'estadual', _('Estadual')), diff --git a/sapl/norma/views.py b/sapl/norma/views.py index fa7c0bbde..dfb670f74 100644 --- a/sapl/norma/views.py +++ b/sapl/norma/views.py @@ -4,10 +4,14 @@ import re from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ObjectDoesNotExist -from django.urls import reverse +from django.db.models import Q from django.http import HttpResponse, JsonResponse +from django.http.response import HttpResponseRedirect from django.template import RequestContext, loader +from django.urls import reverse +from django.urls.base import reverse_lazy from django.utils import timezone +from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ from django.views.generic import TemplateView, UpdateView from django.views.generic.base import RedirectView @@ -184,7 +188,7 @@ class NormaCrud(Crud): public = [RP_LIST, RP_DETAIL] class BaseMixin(Crud.BaseMixin): - list_field_names = ['tipo', 'numero', 'ano', 'ementa'] + list_field_names = ['epigrafe', 'ementa'] list_url = '' @@ -242,14 +246,54 @@ class NormaCrud(Crud): layout_key = 'NormaJuridicaCreate' - class ListView(Crud.ListView, RedirectView): + class ListView(Crud.ListView): + + def get(self, request, *args, **kwargs): + if AppConfig.attr('texto_articulado_norma'): + self.status = self.request.GET.get('status', '') + return Crud.ListView.get(self, request, *args, **kwargs) + else: + url = self.get_redirect_url(*args, **kwargs) + return HttpResponseRedirect(url) + + def hook_header_epigrafe(self, *args, **kwargs): + return force_text(_('Epigrafe')) + + def hook_epigrafe(self, obj, ss, url): + + return obj.epigrafe, reverse_lazy( + 'sapl.norma:norma_ta', + kwargs={'pk': obj.id}) def get_redirect_url(self, *args, **kwargs): namespace = self.model._meta.app_config.name return reverse('%s:%s' % (namespace, 'norma_pesquisa')) - def get(self, request, *args, **kwargs): - return RedirectView.get(self, request, *args, **kwargs) + def get_queryset(self): + if self.status == 'pendente': + qs = NormaJuridica.objects.normas_com_textos_articulados_pendentes() + elif self.status == 'publico': + qs = NormaJuridica.objects.normas_com_textos_articulados_publicados() + else: + qs = NormaJuridica.objects.normas_sem_textos_articulados() + + return qs.order_by('-texto_articulado__privacidade', '-ano', '-numero') + + def get_context_data(self, **kwargs): + context = Crud.ListView.get_context_data(self, **kwargs) + + if self.status == 'pendente': + context['title'] = 'Normas Jurídicas com Textos Articulados não publicados' + elif self.status == 'publico': + context['title'] = 'Normas Jurídicas com Textos Articulados publicados' + else: + context['title'] = 'Normas Jurídicas sem Textos Articulados' + + return context + + @classmethod + def get_url_regex(cls): + return r'^check_compilacao$' class UpdateView(Crud.UpdateView): form_class = NormaJuridicaForm @@ -258,7 +302,8 @@ class NormaCrud(Crud): def get_initial(self): initial = super().get_initial() - norma = NormaJuridica.objects.select_related("materia").get(id=self.kwargs['pk']) + norma = NormaJuridica.objects.select_related( + "materia").get(id=self.kwargs['pk']) if norma.materia: initial['tipo_materia'] = norma.materia.tipo initial['ano_materia'] = norma.materia.ano @@ -328,7 +373,7 @@ def recuperar_norma(request): 'id': norma.id}) except ObjectDoesNotExist: logger.warning('user=' + username + '. NormaJuridica buscada (tipo={}, ano={}, numero={}) não existe. ' - 'Definida com ementa vazia e id 0.'.format(tipo, ano, numero)) + 'Definida com ementa vazia e id 0.'.format(tipo, ano, numero)) response = JsonResponse({'ementa': '', 'id': 0}) return response diff --git a/sapl/templates/navbar.yaml b/sapl/templates/navbar.yaml index aee41fe93..3d323c2de 100644 --- a/sapl/templates/navbar.yaml +++ b/sapl/templates/navbar.yaml @@ -75,6 +75,16 @@ url: 'sapl.norma:normajuridica_create' check_permission: norma.add_normajuridica + - title: {% trans 'Textos Articulados Publicados' %} + url: {% url 'sapl.norma:normajuridica_list'%}?status=publico + check_permission: compilacao.change_dispositivo_edicao_dinamica + - title: {% trans 'Textos Articulados Pendentes' %} + url: {% url 'sapl.norma:normajuridica_list' %}?status=pendente + check_permission: compilacao.change_dispositivo_edicao_dinamica + - title: {% trans 'Textos Articulados não cadastrados' %} + url: {% url 'sapl.norma:normajuridica_list' %}?status=sem_texto + check_permission: compilacao.change_dispositivo_edicao_dinamica + - title: {% trans 'Sistema' %} check_permission: base.menu_sistemas children: @@ -86,7 +96,7 @@ check_permission: user.is_superuser - title: {% trans 'Inconsistências de Dados' %} url: {% url 'sapl.base:lista_inconsistencias' %} - check_permission: user.is_superuser + check_permission: user.is_superuser {% comment %}