From 823fb98edb1dab54ab7679ce3f70925c099335d1 Mon Sep 17 00:00:00 2001 From: Sesostris Vieira Date: Tue, 5 Apr 2016 18:43:47 -0300 Subject: [PATCH] Aprimoramentos no dashboard --- .../home/templates/home/sem_convenio.html | 98 ++++++ sigi/apps/home/urls.py | 8 + sigi/apps/home/views.py | 281 ++++++++++++++---- sigiStatic/css/base_site.css | 12 - sigiStatic/css/style.css | 47 +++ sigiStatic/js/dashboard.js | 63 ++++ templates/index.html | 53 +--- .../snippets/modules/charts-convenios.html | 123 ++++---- .../snippets/modules/resumo_convenios.html | 60 ++++ templates/snippets/modules/resumo_seit.html | 38 +++ 10 files changed, 591 insertions(+), 192 deletions(-) create mode 100644 sigi/apps/home/templates/home/sem_convenio.html create mode 100644 sigiStatic/js/dashboard.js create mode 100644 templates/snippets/modules/resumo_convenios.html create mode 100644 templates/snippets/modules/resumo_seit.html diff --git a/sigi/apps/home/templates/home/sem_convenio.html b/sigi/apps/home/templates/home/sem_convenio.html new file mode 100644 index 0000000..451a586 --- /dev/null +++ b/sigi/apps/home/templates/home/sem_convenio.html @@ -0,0 +1,98 @@ +{% load static from staticfiles %} +{% load i18n %} + + + + + {{ titulo }} + + + + + + + + + + + + + {% for casa in casas %} + + + + + + + {% endfor %} +
{% trans "Nome da Casa" %}{% trans "UF" %}{% trans "Gerente de contas" %}{% trans "Serviços" %}
{{ casa.nome }}{{ casa.municipio.uf.sigla }}{{ casa.gerente_contas.nome_completo }} + {% for s in casa.servico_set.all %} + {% if s.data_desativacao == None %} + {{ s.tipo_servico.nome }}{% if not forloop.last %}, {% endif %} + {% endif %} + {% endfor %} +
+ + + + diff --git a/sigi/apps/home/urls.py b/sigi/apps/home/urls.py index 5d2b601..33e1e5b 100644 --- a/sigi/apps/home/urls.py +++ b/sigi/apps/home/urls.py @@ -4,4 +4,12 @@ from django.conf.urls import patterns, url urlpatterns = patterns('sigi.apps.home.views', url(r'^$', 'index', name='sigi_index'), + url(r'^home/resumoconvenios/$', 'resumo_convenios', name="home_resumoconvenios"), + url(r'^home/resumoseit/$', 'resumo_seit', name="home_resumoseit"), + url(r'^home/chartseit/$', 'chart_seit', name="home_chartseit"), + url(r'^home/chartconvenios/$', 'chart_convenios', name="home_chartconvenios"), + url(r'^home/chartcarteira/$', 'chart_carteira', name="home_chartcarteira"), + url(r'^home/chartperformance/$', 'chart_performance', name="home_chartperformance"), + url(r'^home/report/semconvenio/$', 'report_sem_convenio', name="home_reportsemconvenio"), + ) diff --git a/sigi/apps/home/views.py b/sigi/apps/home/views.py index d0a187f..70b0f17 100644 --- a/sigi/apps/home/views.py +++ b/sigi/apps/home/views.py @@ -23,7 +23,8 @@ # import datetime -from django.shortcuts import render +import calendar +from django.shortcuts import render, get_object_or_404 from django.utils.translation import ugettext as _ from itertools import cycle from sigi.apps.casas.models import CasaLegislativa @@ -31,29 +32,152 @@ from sigi.apps.convenios.models import Convenio, Projeto from sigi.apps.diagnosticos.models import Diagnostico from sigi.apps.metas.models import Meta from sigi.apps.servicos.models import TipoServico +from sigi.apps.servidores.models import Servidor from django.views.decorators.cache import never_cache from django.contrib.auth.decorators import login_required +from django.http.response import JsonResponse +from django.core.urlresolvers import reverse +from django.db.models import Q, Count +from sigi.shortcuts import render_to_pdf @never_cache @login_required def index(request): + context = {'gerentes': Servidor.objects.exclude(casas_que_gerencia=None)} + return render(request, 'index.html', context) + +@never_cache +@login_required +def resumo_convenios(request): + context = {'tabela_resumo_camara': busca_informacoes_camara() } + return render(request, 'snippets/modules/resumo_convenios.html', context) + +@never_cache +@login_required +def resumo_seit(request): + mes = request.GET.get('mes', None) + ano = request.GET.get('ano', None) + + try: + mes = datetime.date(year=int(ano), month=int(mes), day=1) + tabela_resumo_seit = busca_informacoes_seit(mes) + except: + tabela_resumo_seit = busca_informacoes_seit() + + context = {'tabela_resumo_seit': tabela_resumo_seit} + return render(request, 'snippets/modules/resumo_seit.html', context) + +@never_cache +@login_required +def chart_seit(request): + mes = request.GET.get('mes', None) + ano = request.GET.get('ano', None) + + try: + mes = datetime.date(year=int(ano), month=int(mes), day=1) + tabela_resumo_seit = busca_informacoes_seit(mes) + except: + tabela_resumo_seit = busca_informacoes_seit() + + data = { + 'type': 'line', + 'prevlink': reverse('home_chartseit') + ('?ano=%s&mes=%s' % + (tabela_resumo_seit['mes_anterior'].year, + tabela_resumo_seit['mes_anterior'].month)), + 'nextlink': reverse('home_chartseit') + ('?ano=%s&mes=%s' % + (tabela_resumo_seit['proximo_mes'].year, + tabela_resumo_seit['proximo_mes'].month)), + 'options': {'bezierCurve': False, 'datasetFill': False, 'pointDot': False, 'responsive': True}, + 'data': { + 'labels': ['%02d/%s' % (mes.month, mes.year) for mes in reversed(tabela_resumo_seit['meses'])], + 'datasets': [ + { + 'label': servico['nome'], + 'strokeColor': servico['cor'], + 'data': [mes['total'] for mes in reversed(servico['novos_por_mes'])] + } + for servico in tabela_resumo_seit['servicos']], + } + } + + return JsonResponse(data) + +@never_cache +@login_required +def chart_convenios(request): + q = request.GET.get('q', 'all') convenios = Convenio.objects.all() - convenios_assinados = convenios.exclude(data_retorno_assinatura=None) + if q == 'assinados': + convenios = convenios.exclude(data_retorno_assinatura=None) + data = { + 'type': 'pie', + 'options': {'responsive': False, 'maintainAspectRatio': False}, + 'data': grafico_convenio_projeto(convenios), + } + return JsonResponse(data) - tabela_resumo_camara = busca_informacoes_camara() - tabela_resumo_seit = busca_informacoes_seit() - tabela_resumo_diagnostico = busca_informacoes_diagnostico() - dados_graficos_convenio_projeto = [(1, grafico_convenio_projeto(convenios)), - (2, grafico_convenio_projeto(convenios_assinados))] - context = { - 'tabela_resumo_camara': tabela_resumo_camara, - 'tabela_resumo_seit': tabela_resumo_seit, - 'tabela_resumo_diagnostico': tabela_resumo_diagnostico, - 'dados_graficos_convenio_projeto': dados_graficos_convenio_projeto, - 'metas': Meta.objects.all(), +@never_cache +@login_required +def chart_carteira(request): + colors, highlights = color_palete() + data = {'type': 'pie', + 'options': {'responsive': True}, + 'data': [{'value': r['total_casas'], + 'color': colors.next(), + 'highlight': highlights.next(), + 'label': Servidor.objects.get(pk=r['gerente_contas']).nome_completo + } + for r in CasaLegislativa.objects.all().values('gerente_contas').annotate(total_casas=Count('pk')).order_by('gerente_contas') + ] } - return render(request, 'index.html', context) + + return JsonResponse(data) +@never_cache +@login_required +def chart_performance(request): + servidor = request.GET.get('servidor', None) + + if servidor is None: + casas = CasaLegislativa.objects.exclude(gerente_contas=None) + else: + gerente = get_object_or_404(Servidor, pk=servidor) + casas = gerente.casas_que_gerencia + + data = { + 'type': 'pie', + 'options': {'responsive': True}, + 'data': [ + {'label': _(u"Utilizam serviços"), 'value': casas.exclude(servico=None).count(), 'color': '#91e8e1'}, + {'label': _(u"Não utilizam serviços"), 'value': casas.filter(servico=None).count(), 'color': '#f7a35c'}, + ] + } + + return JsonResponse(data) + +@never_cache +@login_required +def report_sem_convenio(request): + modo = request.GET.get('modo', None) + + sc = sem_convenio() + + if modo == 'H': + casas = sc['hospedagem'] + titulo = _(u"Casas sem convenio que utilizam algum serviço de hospedagem") + elif modo == 'R': + casas = sc['registro'] + titulo = _(u"Casas sem convenio que utilizam somente serviço de registro") + else: + casas = sc['total'] + titulo = _(u"Casas sem convenio que utilizam algum serviço de registro e/ou hospedagem") + + context = {'casas': casas, 'titulo': titulo} + print context +# return render(request, 'home/sem_convenio.html', context) + return render_to_pdf('home/sem_convenio.html', context) + + def busca_informacoes_camara(): """ Busca informacoes no banco para montar tabela de resumo de camaras por projeto @@ -128,49 +252,38 @@ def busca_informacoes_camara(): # Unindo as duas listass para que o cabecalho da esquerda fique junto com sua # respectiva linha lista_zip = zip(cabecalho_esquerda, linhas) - + # Retornando listas em forma de dicionario return { - u'cabecalho_topo': cabecalho_topo, - u'lista_zip': lista_zip, - u'total_camaras': camaras.count(), - u'camaras_sem_processo': camaras_sem_processo.count(), + 'cabecalho_topo': cabecalho_topo, + 'lista_zip': lista_zip, + 'total_camaras': camaras.count(), + 'camaras_sem_processo': camaras_sem_processo.count(), + 'sem_convenio': sem_convenio(), } -def grafico_convenio_projeto(convenios): - - colors = cycle(['#7cb5ec', - '#434348', - '#90ed7d', - '#f7a35c', - '#8085e9', - '#f15c80', - '#e4d354', - '#8085e8', - '#8d4653', - '#91e8e1', ]) - - highlights = cycle(['#B0D3F4', - '#8E8E91', - '#BCF4B1', - '#FAC89D', - '#B3B6F2', - '#F79DB3', - '#EFE598', - '#B3B6F1', - '#BB9098', - '#BDF1ED', ]) +def sem_convenio(): + total = CasaLegislativa.objects.exclude(servico=None).filter(servico__data_desativacao=None, convenio=None).order_by('municipio__uf__sigla', 'nome').distinct('municipio__uf__sigla', 'nome') + hospedagem = CasaLegislativa.objects.exclude(servico=None).filter(servico__data_desativacao=None, servico__tipo_servico__modo='H', convenio=None).order_by('municipio__uf__sigla', 'nome').distinct('municipio__uf__sigla', 'nome') + reg_keys = set(total.values_list('pk', flat=True)).difference(set(hospedagem.values_list('pk', flat=True))) + registro = CasaLegislativa.objects.filter(pk__in=reg_keys).order_by('municipio__uf__sigla', 'nome') + return { + 'total': total, + 'hospedagem': hospedagem, + 'registro': registro, + } +def grafico_convenio_projeto(convenios): + colors, highlights = color_palete() projetos = Projeto.objects.all() - - lista_projetos = [(projeto.sigla, - convenios.filter(projeto=projeto).count(), - colors.next(), - highlights.next()) + lista_projetos = [{'label': projeto.sigla, + 'value': convenios.filter(projeto=projeto).count(), + 'color': colors.next(), + 'highlight': highlights.next()} for projeto in projetos] # remove projetos sem convenio - lista_projetos = [x for x in lista_projetos if x[1] > 0] + lista_projetos = [x for x in lista_projetos if x['value'] > 0] # print lista_projetos # total_convenios = "Total: " + str(convenios.count()) @@ -178,27 +291,45 @@ def grafico_convenio_projeto(convenios): return lista_projetos -def busca_informacoes_seit(): - mes_atual = datetime.date.today().replace(day=1) +def busca_informacoes_seit(mes_atual=None): + colors, highlights = color_palete() + if mes_atual is None: + mes_atual = datetime.date.today().replace(day=1) mes_anterior = mes_atual - datetime.timedelta(days=1) - - result = [{'nome': '', - 'total': 'Total de casas atendidas', - 'novos_mes_anterior': 'Novas casas em %s/%s' % (mes_anterior.month, mes_anterior.year), - 'novos_mes_atual': 'Novas casas em %s/%s' % (mes_atual.month, mes_atual.year)}] + proximo_mes = mes_atual + datetime.timedelta(days=calendar.monthrange(mes_atual.year, mes_atual.month)[1]) + + meses = [] + mes = mes_atual + for i in range(1, 13): + meses.append(mes) + mes = (mes - datetime.timedelta(days=1)).replace(day=1) + + result = { + 'mes_atual': mes_atual, + 'mes_anterior': mes_anterior, + 'proximo_mes': proximo_mes, + 'meses': meses, + 'titulos': [ '', + 'Total de casas atendidas', + 'Novas casas em %s/%s' % (mes_anterior.month, mes_anterior.year), + 'Novas casas em %s/%s' % (mes_atual.month, mes_atual.year) + ], + 'servicos': [], + } for tipo_servico in TipoServico.objects.all(): por_mes = [] - for mes in range(1, 13): - por_mes.append({'mes': '%02d/%s' % (mes, datetime.date.today().year), - 'total': tipo_servico.servico_set.filter(data_desativacao=None, data_ativacao__year=mes_atual.year, data_ativacao__month=mes).count()}) + for mes in meses: + por_mes.append({'mes': '%02d/%s' % (mes.month, mes.year), + 'total': tipo_servico.servico_set.filter(data_ativacao__year=mes.year, data_ativacao__month=mes.month).count()}) - result.append( + result['servicos'].append( {'nome': tipo_servico.nome, - 'total': tipo_servico.servico_set.filter(data_desativacao=None).count(), - 'novos_mes_anterior': tipo_servico.servico_set.filter(data_desativacao=None, data_ativacao__year=mes_anterior.year, data_ativacao__month=mes_anterior.month).count(), - 'novos_mes_atual': tipo_servico.servico_set.filter(data_desativacao=None, data_ativacao__year=mes_atual.year, data_ativacao__month=mes_atual.month).count(), + 'total': tipo_servico.servico_set.filter(Q(data_ativacao__lt=proximo_mes)&(Q(data_desativacao=None)|Q(data_desativacao__gt=proximo_mes))).count(), + 'novos_mes_anterior': tipo_servico.servico_set.filter(data_ativacao__year=mes_anterior.year, data_ativacao__month=mes_anterior.month).count(), + 'novos_mes_atual': tipo_servico.servico_set.filter(data_ativacao__year=mes_atual.year, data_ativacao__month=mes_atual.month).count(), 'novos_por_mes': por_mes, + 'cor': colors.next(), } ) @@ -210,3 +341,29 @@ def busca_informacoes_diagnostico(): {'title': _(u'Diagnósticos digitados'), 'count': Diagnostico.objects.count()}, {'title': _(u'Diagnósticos publicados'), 'count': Diagnostico.objects.filter(publicado=True).count()}, ] + + +def color_palete(): + colors = cycle(['#7cb5ec', + '#434348', + '#90ed7d', + '#f7a35c', + '#8085e9', + '#f15c80', + '#e4d354', + '#8085e8', + '#8d4653', + '#91e8e1', ]) + + highlights = cycle(['#B0D3F4', + '#8E8E91', + '#BCF4B1', + '#FAC89D', + '#B3B6F2', + '#F79DB3', + '#EFE598', + '#B3B6F1', + '#BB9098', + '#BDF1ED', ]) + + return (colors, highlights) diff --git a/sigiStatic/css/base_site.css b/sigiStatic/css/base_site.css index f8b1c52..830ad95 100644 --- a/sigiStatic/css/base_site.css +++ b/sigiStatic/css/base_site.css @@ -124,18 +124,6 @@ div#footer a { width: 18px; } -.titlemapbox { - display: inline-block; - margin-bottom: 0.6em; - width: 100%; -} - -.mapbox { - text-align: center; - display: inline-block; - float: right; -} - #branding h1 { float: left; } diff --git a/sigiStatic/css/style.css b/sigiStatic/css/style.css index 5ec83e2..385ed63 100644 --- a/sigiStatic/css/style.css +++ b/sigiStatic/css/style.css @@ -1,3 +1,8 @@ +.chartcontainer { + position: relative; + max-height: 400px; +} + div[id^=canvas] { padding: 20px 20px 20px 20px; min-height: 180px; @@ -33,4 +38,46 @@ div[id^=canvas] { -moz-transition: background-color 200ms ease-in-out; -o-transition: background-color 200ms ease-in-out; transition: background-color 200ms ease-in-out; +} +.line-legend { + list-style: none; + position: absolute; + right: 50px; + top: 18px; +} +.line-legend li span { + display: block; + position: absolute; + left: 0; + top: 0; + width: 20px; + height: 100%; + border-radius: 5px; +} +.line-legend li { + display: block; + padding-left: 30px; + position: relative; + margin-bottom: 4px; + border-radius: 5px; + padding: 2px 8px 2px 28px; + font-size: 14px; + cursor: default; + -webkit-transition: background-color 200ms ease-in-out; + -moz-transition: background-color 200ms ease-in-out; + -o-transition: background-color 200ms ease-in-out; + transition: background-color 200ms ease-in-out; +} + +.numeros td, .numeros th { + text-align: right; +} +.numeros tr :first-child { + text-align: left; +} +.servicos tr :first-child { + width: 40%; +} +.panel-footer dl { + margin-bottom: 5px; } \ No newline at end of file diff --git a/sigiStatic/js/dashboard.js b/sigiStatic/js/dashboard.js new file mode 100644 index 0000000..bf4f445 --- /dev/null +++ b/sigiStatic/js/dashboard.js @@ -0,0 +1,63 @@ +$(document).ready(function () { + setlinks(); + $("div[data-source]").each(function(index, container) { + var container = $(container); + var url = container.attr('data-source'); + get_content(container, url); + }); + $("canvas[data-source]").each(function(index, canvas) { + var canvas = $(canvas) + var url = canvas.attr("data-source"); + plot_chart(canvas, url); + }); +}); + +function setlinks() { + $("a[data-target]").off('click').on('click', function(e) { + e.preventDefault(); + var $this = $(this); + var target = $("#"+$this.attr('data-target')); + var url = $this.attr('href'); + if (target.is("canvas")) { + plot_chart(target, url); + } else if (target.is("div")) { + get_content(target, url); + } + }); +} + +function get_content(container, url) { + $.get(url, function(data) { + container.html(data); + setlinks(); + }); +} + +function plot_chart(canvas, url) { + $.get(url, function(data) { + var new_canvas = $(canvas.clone()).insertBefore(canvas); + canvas.remove(); + canvas = new_canvas; + var ctx = canvas.get(0).getContext("2d"); + if (data.type == 'pie') { + var myChart = new Chart(ctx).Pie(data.data, data.options); + } else if (data.type == 'line') { + var myChart = new Chart(ctx).Line(data.data, data.options); + } else if (data.type == 'bar') { + var myChart = new Chart(ctx).Bar(data.data, data.options); + } + + if (canvas.is("[data-legend-id]")) { + var legend_container = $("#"+canvas.attr("data-legend-id")); + legend_container.html(myChart.generateLegend()); + } + if (canvas.is("[data-prevlink-id]")) { + var prevlink = $("#"+canvas.attr("data-prevlink-id")); + prevlink.attr('href', data.prevlink); + } + if (canvas.is("[data-nextlink-id]")) { + var nextlink = $("#"+canvas.attr("data-nextlink-id")); + nextlink.attr('href', data.nextlink); + } + }); +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 3e332a2..432ef16 100644 --- a/templates/index.html +++ b/templates/index.html @@ -12,55 +12,14 @@ - - - + {% endblock %} -{% block content_title %}

{% trans 'Dashboard' %}

{% endblock %} +{% block content_title %} +

{% trans 'Dashboard' %}

+
{% trans 'Mapa de atuação do Interlegis' %}
+{% endblock %} {% block extrastyle %}{{ block.super }}{% endblock %} @@ -74,4 +33,4 @@
{% include "snippets/modules/charts-convenios.html" %}
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/templates/snippets/modules/charts-convenios.html b/templates/snippets/modules/charts-convenios.html index fd030d1..2aba7d1 100644 --- a/templates/snippets/modules/charts-convenios.html +++ b/templates/snippets/modules/charts-convenios.html @@ -2,94 +2,75 @@ {% load i18n %}
+
-
{% trans 'Resumo de informações' %}
-
-
-
{% trans 'Resumo por região' %}:
-
- -
-
- - - {% for item in tabela_resumo_camara.cabecalho_topo %} - - {% endfor %} - - {% for cabecalho,lista in tabela_resumo_camara.lista_zip %} - - - {% for item in lista %} - - {% endfor %} - - {% endfor %} -
{{item}}
{{cabecalho}}{{item}}
+
{% trans 'Sazonalidade da hospedagem de serviços' %}
+
+
+ +
+ +
- +
+
+ +
+
+
+
+
{% trans 'Performance da gerência de carteiras' %}
+
+ +
+ +
+
+
+
+
-
{% trans 'Serviços hospedados no Interlegis (SEIT)' %}
+
{% trans 'Distribuição de Casas por Gerente' %}
-
- +
+ +
- - {% for servico in tabela_resumo_seit %} - - {% if forloop.first %} - - - - - {% else %} - - - - - {% endif %} - - {% endfor %} -
{{ servico.nome }}{{ servico.total }}{{ servico.novos_mes_anterior }}{{ servico.novos_mes_atual }} - {{ servico.nome }} - - {% for mes in servico.novos_por_mes %} - - {% endfor %} - - {{ servico.total }}{{ servico.novos_mes_anterior }}{{ servico.novos_mes_atual }}
+{% comment %}
{% trans 'Convênios assinados por projeto' %}
-
- +
+
+ +
+
@@ -99,13 +80,13 @@
{% trans 'Processos de convênios por projeto' %}
-
- +
+ +
-
- +{% endcomment %} diff --git a/templates/snippets/modules/resumo_convenios.html b/templates/snippets/modules/resumo_convenios.html new file mode 100644 index 0000000..a0fcb2d --- /dev/null +++ b/templates/snippets/modules/resumo_convenios.html @@ -0,0 +1,60 @@ +{% load i18n %} + +
+
{% trans 'Resumo de informações' %}
+
+
+
{% trans 'Resumo por região' %}:
+
+ +
+
+ + + {% for item in tabela_resumo_camara.cabecalho_topo %} + + {% endfor %} + + {% for cabecalho,lista in tabela_resumo_camara.lista_zip %} + + + {% for item in lista %} + + {% endfor %} + + {% endfor %} +
{{item}}
{{cabecalho}}{{item}}
+
+ +
\ No newline at end of file diff --git a/templates/snippets/modules/resumo_seit.html b/templates/snippets/modules/resumo_seit.html new file mode 100644 index 0000000..9d9154b --- /dev/null +++ b/templates/snippets/modules/resumo_seit.html @@ -0,0 +1,38 @@ +{% load static from staticfiles %} +{% load i18n %} + +
+
{% trans 'Serviços hospedados no Interlegis (SEIT)' %}
+
+ + + {% for s in tabela_resumo_seit.titulos %} + + {% endfor %} + + {% for servico in tabela_resumo_seit.servicos %} + + + + + + + {% endfor %} +
{{ s }}
+ {{ servico.nome }} + + {% for mes in servico.novos_por_mes %} + + {% endfor %} + + {{ servico.total }}{{ servico.novos_mes_anterior }}{{ servico.novos_mes_atual }}
+ +
+