Browse Source

refactor: Relatório de Estatísticas de Normas

- inclui no filtro meses e mais acessadas
- altera layout dos resultados html e pdf
- cria view table para otmizar e agilizar a computação dos dados
pull/3605/head
LeandroJatai 2 years ago
parent
commit
148cf63a1e
  1. 37
      sapl/base/forms.py
  2. 48
      sapl/base/views.py
  3. 56
      sapl/norma/migrations/0042_norma_viewnormasestatisticas.py
  4. 35
      sapl/norma/models.py
  5. 64
      sapl/templates/base/EstatisticasAcessoNormas_filter.html
  6. 2
      sapl/templates/relatorios/base_relatorio.html
  7. 62
      sapl/templates/relatorios/relatorio_estatisticas_acesso_normas.html

37
sapl/base/forms.py

@ -28,7 +28,7 @@ from sapl.crispy_layout_mixin import (form_actions, to_column, to_row,
from sapl.materia.models import (DocumentoAcessorio, MateriaEmTramitacao, from sapl.materia.models import (DocumentoAcessorio, MateriaEmTramitacao,
MateriaLegislativa, UnidadeTramitacao, MateriaLegislativa, UnidadeTramitacao,
StatusTramitacao) StatusTramitacao)
from sapl.norma.models import NormaJuridica from sapl.norma.models import NormaJuridica, NormaEstatisticas
from sapl.parlamentares.models import Partido, SessaoLegislativa,\ from sapl.parlamentares.models import Partido, SessaoLegislativa,\
Parlamentar, Votante Parlamentar, Votante
from sapl.protocoloadm.models import DocumentoAdministrativo from sapl.protocoloadm.models import DocumentoAdministrativo
@ -40,7 +40,7 @@ from sapl.utils import (autor_label, autor_modal, ChoiceWithoutValidationField,
FilterOverridesMetaMixin, FileFieldCheckMixin, FilterOverridesMetaMixin, FileFieldCheckMixin,
ImageThumbnailFileInput, qs_override_django_filter, ImageThumbnailFileInput, qs_override_django_filter,
RANGE_ANOS, YES_NO_CHOICES, choice_tipos_normas, RANGE_ANOS, YES_NO_CHOICES, choice_tipos_normas,
GoogleRecapthaMixin, parlamentares_ativos) GoogleRecapthaMixin, parlamentares_ativos, RANGE_MESES)
from .models import AppConfig, CasaLegislativa from .models import AppConfig, CasaLegislativa
@ -903,11 +903,11 @@ class RelatorioNormasMesFilterSet(django_filters.FilterSet):
buttons = FormActions( buttons = FormActions(
*[ *[
HTML(''' HTML('''
<div class="form-check"> <div class="form-check col-auto">
<input name="relatorio" type="checkbox" class="form-check-input" id="relatorio"> <input name="relatorio" type="checkbox" class="form-check-input" id="relatorio">
<label class="form-check-label" for="relatorio">Gerar relatório PDF</label> <label class="form-check-label" for="relatorio">Gerar relatório PDF</label>
</div> </div>
''') ''')
], ],
Submit('pesquisar', _('Pesquisar'), css_class='float-right', Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'), onclick='return true;'),
@ -934,14 +934,29 @@ class EstatisticasAcessoNormasForm(Form):
choices=RANGE_ANOS, choices=RANGE_ANOS,
initial=timezone.now().year) initial=timezone.now().year)
mes = forms.ChoiceField(required=False,
label='Mês de acesso',
choices=[('', 'Todos os Meses')] + RANGE_MESES,
initial='')
mais_acessadas = forms.ChoiceField(required=False,
label='Mais Acessadas',
choices=[
(5, '005 mais acessadas'),
(10, '010 mais acessadas'),
(50, '050 mais acessadas'),
(100, '100 mais acessadas'),
],
initial=5)
class Meta: class Meta:
fields = ['ano'] fields = ['ano', 'mes', 'mais_acessadas']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(EstatisticasAcessoNormasForm, self).__init__( super(EstatisticasAcessoNormasForm, self).__init__(
*args, **kwargs) *args, **kwargs)
row1 = to_row([('ano', 12)]) row1 = to_row([('ano', 3), ('mes', 6), ('mais_acessadas', 3), ])
buttons = FormActions( buttons = FormActions(
*[ *[
@ -963,6 +978,10 @@ class EstatisticasAcessoNormasForm(Form):
Fieldset(_('Normas por acessos nos meses do ano.'), Fieldset(_('Normas por acessos nos meses do ano.'),
row1, buttons) row1, buttons)
) )
self.fields['ano'].choices = NormaEstatisticas.objects.order_by(
'-ano').distinct().values_list('ano', 'ano') or [
(timezone.now().year, timezone.now().year)
]
def clean(self): def clean(self):
super(EstatisticasAcessoNormasForm, self).clean() super(EstatisticasAcessoNormasForm, self).clean()

48
sapl/base/views.py

@ -48,7 +48,8 @@ from sapl.crud.base import CrudAux, make_pagination, Crud,\
from sapl.materia.models import (Anexada, Autoria, DocumentoAcessorio, MateriaEmTramitacao, MateriaLegislativa, from sapl.materia.models import (Anexada, Autoria, DocumentoAcessorio, MateriaEmTramitacao, MateriaLegislativa,
Proposicao, StatusTramitacao, TipoDocumento, TipoMateriaLegislativa, UnidadeTramitacao, Proposicao, StatusTramitacao, TipoDocumento, TipoMateriaLegislativa, UnidadeTramitacao,
MateriaAssunto) MateriaAssunto)
from sapl.norma.models import NormaJuridica, TipoNormaJuridica from sapl.norma.models import NormaJuridica, TipoNormaJuridica,\
NormaEstatisticas, ViewNormasEstatisticas
from sapl.parlamentares.models import ( from sapl.parlamentares.models import (
Filiacao, Legislatura, Mandato, Parlamentar, SessaoLegislativa) Filiacao, Legislatura, Mandato, Parlamentar, SessaoLegislativa)
from sapl.protocoloadm.models import (Anexado, DocumentoAdministrativo, Protocolo, StatusTramitacaoAdministrativo, from sapl.protocoloadm.models import (Anexado, DocumentoAdministrativo, Protocolo, StatusTramitacaoAdministrativo,
@ -1191,7 +1192,7 @@ class EstatisticasAcessoNormas(TemplateView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
context = super(EstatisticasAcessoNormas, context = super(EstatisticasAcessoNormas,
self).get_context_data(**kwargs) self).get_context_data(**kwargs)
context['title'] = _('Normas') context['title'] = _('Estatísticas de Acesso às Normas Jurídicas')
form = EstatisticasAcessoNormasForm(request.GET or None) form = EstatisticasAcessoNormasForm(request.GET or None)
context['form'] = form context['form'] = form
@ -1200,33 +1201,32 @@ class EstatisticasAcessoNormas(TemplateView):
return self.render_to_response(context) return self.render_to_response(context)
context['ano'] = self.request.GET['ano'] context['ano'] = self.request.GET['ano']
context['mes'] = self.request.GET.get('mes', '')
query = ''' context['mais_acessadas'] = int(
select norma_id, ano, extract(month from horario_acesso) as mes, count(*) self.request.GET.get('mais_acessadas', 5))
from norma_normaestatisticas
where ano = {} if not context['mes']:
group by mes, ano, norma_id context['mais_acessadas'] = 10
order by mes desc;
'''.format(context['ano']) params = {
cursor = connection.cursor() 'ano_est': context['ano'],
cursor.execute(query) 'mais_acessadas__lte': context['mais_acessadas']
rows = cursor.fetchall() }
if context['mes']:
params['mes_est'] = context['mes']
estatisticas = ViewNormasEstatisticas.objects.filter(
**params
)
normas_mes = collections.OrderedDict() normas_mes = collections.OrderedDict()
meses = {1: 'Janeiro', 2: 'Fevereiro', 3: 'Março', 4: 'Abril', 5: 'Maio', 6: 'Junho', meses = {1: 'Janeiro', 2: 'Fevereiro', 3: 'Março', 4: 'Abril', 5: 'Maio', 6: 'Junho',
7: 'Julho', 8: 'Agosto', 9: 'Setembro', 10: 'Outubro', 11: 'Novembro', 12: 'Dezembro'} 7: 'Julho', 8: 'Agosto', 9: 'Setembro', 10: 'Outubro', 11: 'Novembro', 12: 'Dezembro'}
for row in rows: for norma in estatisticas:
if not meses[int(row[2])] in normas_mes: if not meses[norma.mes_est] in normas_mes:
normas_mes[meses[int(row[2])]] = [] normas_mes[meses[norma.mes_est]] = []
norma_est = [NormaJuridica.objects.get(id=row[0]), row[3]] normas_mes[meses[norma.mes_est]].append(norma)
normas_mes[meses[int(row[2])]].append(norma_est)
# Ordena por acesso e limita em 5
for n in normas_mes:
sorted_by_value = sorted(
normas_mes[n], key=lambda kv: kv[1], reverse=True)
normas_mes[n] = sorted_by_value[0:5]
context['normas_mes'] = normas_mes context['normas_mes'] = normas_mes

56
sapl/norma/migrations/0042_norma_viewnormasestatisticas.py

@ -0,0 +1,56 @@
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('norma', '0041_auto_20220814_2235'),
]
operations = [
migrations.RunSQL("""
drop view if exists norma_viewnormasestatisticas;
create or replace view norma_viewnormasestatisticas as
select row_number() OVER() as id, * from (
SELECT
ROW_NUMBER() OVER (
PARTITION BY ano_est, mes_est
ORDER BY ano_est, mes_est desc, norma_count desc
)::smallint AS "mais_acessadas"
,
*
from (
SELECT
"norma_normaestatisticas"."norma_id" as norma_id,
extract(year from horario_acesso) as ano_est,
extract(month from horario_acesso) as mes_est,
count(*) as norma_count,
"norma_normajuridica"."numero" as norma_numero,
"norma_normajuridica"."ano" as norma_ano,
"norma_normajuridica"."data" as norma_data,
"norma_tiponormajuridica"."sigla" as norma_tipo_sigla,
"norma_tiponormajuridica"."descricao" as norma_tipo_descricao,
"norma_normajuridica"."ementa" as norma_ementa,
"norma_normajuridica"."observacao" as norma_observacao
from norma_normaestatisticas
INNER JOIN "norma_normajuridica" ON ("norma_normaestatisticas"."norma_id" = "norma_normajuridica"."id")
INNER JOIN "norma_tiponormajuridica" ON ("norma_normajuridica"."tipo_id" = "norma_tiponormajuridica"."id")
group by
"norma_normaestatisticas"."norma_id",
ano_est,
mes_est,
norma_numero,
norma_ano,
norma_data,
norma_ementa,
norma_observacao,
norma_tipo_sigla,
norma_tipo_descricao
order by ano_est, mes_est desc, norma_count desc, norma_ano desc
) as subquery
) as query_final
order by ano_est, mes_est desc, mais_acessadas;
"""),
]

35
sapl/norma/models.py

@ -311,6 +311,41 @@ class NormaEstatisticas(models.Model):
'usuario': self.usuario, 'norma': self.norma} 'usuario': self.usuario, 'norma': self.norma}
class ViewNormasEstatisticas(models.Model):
mais_acessadas = models.PositiveSmallIntegerField(
verbose_name=_('Mais Acessadas'))
ano_est = models.PositiveSmallIntegerField(
verbose_name=_('Ano do Registro de Acesso'))
mes_est = models.PositiveSmallIntegerField(
verbose_name=_('Mês do Registro de Acesso'))
norma_id = models.BigIntegerField(verbose_name=_('Id da Norma'))
norma_count = models.PositiveSmallIntegerField(
verbose_name=_('Mês do Registro de Acesso'))
norma_numero = models.CharField(
max_length=8, verbose_name=_('Número da Norma'))
norma_ano = models.PositiveSmallIntegerField(
verbose_name=_('Ano da Norma'))
norma_ementa = models.TextField(verbose_name=_('Ementa'))
norma_observacao = models.TextField(
blank=True, verbose_name=_('Observação'))
norma_tipo_sigla = models.CharField(
max_length=3,
verbose_name=_('Sigla do Tipo da Norma'))
norma_tipo_descricao = models.CharField(
max_length=50, verbose_name=_('Descrição do Tipo da Norma'))
norma_data = models.DateField(verbose_name=_('Data da Norma'))
class Meta:
managed = False
db_table = "norma_viewnormasestatisticas"
@reversion.register() @reversion.register()
class AutoriaNorma(models.Model): class AutoriaNorma(models.Model):
autor = models.ForeignKey(Autor, autor = models.ForeignKey(Autor,

64
sapl/templates/base/EstatisticasAcessoNormas_filter.html

@ -19,30 +19,36 @@
{% else %} {% else %}
{% for mes, normas in normas_mes.items %} {% for mes, normas in normas_mes.items %}
<div style="overflow:auto; "> <div style="overflow:auto; ">
<table class="table table-bordered table-hover" style="margin-bottom: 0px;"> <table class="table table-bordered table-hover" style="width:100%; margin-bottom: 30px;">
<thead class="thead-default"> <thead class="thead-default" >
<tr> <tr>
<th><h3 style="text-align:center;">Mês: {{ mes }}</h3></th> <th colspan=3><h3 style="text-align:center;">Mês: {{ mes }}</h3></th>
</tr> </tr>
</thead>
</table>
<table class="table table-bordered table-hover" style="width:100%; margin-bottom: 30px;">
<thead class="thead-default" >
<tr class="active"> <tr class="active">
<th>Norma</th> <th>Posição</th>
<th>Ementa</th>
<th>Acessos</th> <th>Acessos</th>
<th>Norma</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for n in normas %} {% for n in normas %}
{% if n.1 > 0 %} {% if n.norma_count > 0 %}
<tr> <tr>
<td><a href="{% url 'sapl.norma:normajuridica_detail' n.0.pk %}"> <td align="center">{{n.mais_acessadas}}º</td>
{{n.0.tipo.descricao}} - {{n.0.tipo.sigla}} {{n.0.numero}}/{{n.0.ano}} <td align="center">{{n.norma_count}}</td>
</a></td> <td>
<td>{{n.0.ementa}}<br>{{n.0.observacao}}</td> <a href="{% url 'sapl.norma:normajuridica_detail' n.norma_id %}">
<td>{{n.1}}</td> {{n.norma_tipo_descricao}} nº {{n.norma_numero}}, de {{n.norma_data}}
</a>
<br>{{n.norma_ementa}}
{% if n.norma_observacao %}
<small>
<i>
<br><strong>Observações:</strong> {{n.norma_observacao}}
</i>
</small>
{% endif %}
</td>
</tr> </tr>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
@ -53,3 +59,27 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endblock base_content %} {% endblock base_content %}
{% block extra_js %}
<script type="text/javascript">
$(document).ready(function(){
$('#id_mes').change(function(event) {
if (event.currentTarget.selectedOptions[0].value === '') {
$('#id_mais_acessadas').val('5')
$('#id_mais_acessadas')[0].options[2].setAttribute('disabled', true)
$('#id_mais_acessadas')[0].options[3].setAttribute('disabled', true)
} else {
$('#id_mais_acessadas')[0].options[2].removeAttribute('disabled')
$('#id_mais_acessadas')[0].options[3].removeAttribute('disabled')
}
//$('#id_mais_acessadas').prop('disabled', event.currentTarget.selectedOptions[0].value === '')
}).trigger('change')
})
</script>
{% endblock extra_js %}

2
sapl/templates/relatorios/base_relatorio.html

@ -34,6 +34,8 @@
string-set: title content(); string-set: title content();
} }
} }
{% block head_extra_css %}
{% endblock head_extra_css %}
</style> </style>
<link rel="stylesheet" href="{% static '/sapl/css/relatorio.css'%}"> <link rel="stylesheet" href="{% static '/sapl/css/relatorio.css'%}">
</head> </head>

62
sapl/templates/relatorios/relatorio_estatisticas_acesso_normas.html

@ -3,6 +3,32 @@
{% load common_tags %} {% load common_tags %}
{% load static %} {% load static %}
{% block head_extra_css %}
@page {
margin-left: 1.5cm;
margin-right: 1.5cm;
}
table {
border-collapse: collapse;
font-size: 10pt;
margin-bottom: 30px
}
td, th {
border: 1px solid black;
padding: 5px;
vertical-align: middle;
}
th {
text-align: center;
padding: 10px 3px;
}
td:nth-child(1), td:nth-child(2) {
text-align: center;
}
{% endblock head_extra_css %}
{% block content %} {% block content %}
<h2>Estatísticas de acesso de normas</h2> <h2>Estatísticas de acesso de normas</h2>
@ -15,30 +41,36 @@
{% else %} {% else %}
{% for mes, normas in normas_mes.items %} {% for mes, normas in normas_mes.items %}
<div style="overflow:auto; "> <div style="overflow:auto; ">
<table class="table table-bordered table-hover" style="margin-bottom: 0px;"> <table class="table table-bordered">
<thead class="thead-default"> <thead class="thead-default">
<tr> <tr>
<th><h3 style="text-align:center;">Mês: {{ mes }}</h3></th> <th colspan=3><h3>Mês: {{ mes }}</h3></th>
</tr> </tr>
</thead>
</table>
<table class="table table-bordered table-hover" style="width:100%; margin-bottom: 30px;">
<thead class="thead-default" >
<tr class="active"> <tr class="active">
<th>Norma</th> <th>Posição</th>
<th>Ementa</th>
<th>Acessos</th> <th>Acessos</th>
<th>Norma</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for n in normas %} {% for n in normas %}
{% if n.1 > 0 %} {% if n.norma_count > 0 %}
<tr> <tr>
<td>{{n.mais_acessadas}}º</td>
<td>{{n.norma_count}}</td>
<td> <td>
{{n.0.tipo.descricao}} - {{n.0.tipo.sigla}} {{n.0.numero}}/{{n.0.ano}} <strong>
{{n.norma_tipo_descricao}} nº {{n.norma_numero}}, de {{n.norma_data}}
</strong>
<br>{{n.norma_ementa}}
{% if n.norma_observacao %}
<small>
<i>
<br><strong>Observações:</strong> {{n.norma_observacao}}
</i>
</small>
{% endif %}
</td> </td>
<td>{{n.0.ementa}}<br>{{n.0.observacao}}</td>
<td>{{n.1}}</td>
</tr> </tr>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

Loading…
Cancel
Save