diff --git a/sigi/apps/eventos/templates/eventos/alocacao_equipe.html b/sigi/apps/eventos/templates/eventos/alocacao_equipe.html index 0ac47c3..43a1131 100644 --- a/sigi/apps/eventos/templates/eventos/alocacao_equipe.html +++ b/sigi/apps/eventos/templates/eventos/alocacao_equipe.html @@ -1,16 +1,21 @@ {% extends "admin/base_site.html" %} -{% load i18n admin_static %} -{% load static from staticfiles %} -{% load thumbnail %} +{% load static i18n %} {% block extrastyle %} - - {{ block.super }} +{{ block.super }} + + {% endblock %} {% block extrahead %} @@ -19,20 +24,56 @@ {% block coltype %}colMS{% endblock %} -{% block content_title %}

{% blocktrans %}Alocação de equipe em {{ ano_pesquisa }}{% endblocktrans %}

{% endblock %} - -{% block object-tools-items %} - {% if prev_button %} -
  • {% trans "Ano anterior" %}
  • +{% block content_title %} + {% if mes_pesquisa %} + {% for mes in meses %} + {% if forloop.counter == mes_pesquisa %} +
    {% blocktrans with ano=ano_pesquisa|safe mes=mes %}Alocação de equipe em {{ mes }} de {{ ano }}{% endblocktrans %}
    + {% endif %} + {% endfor %} + {% else %} +
    {% blocktrans with ano=ano_pesquisa|safe %}Alocação de equipe em {{ ano }}{% endblocktrans %}
    {% endif %} - {% if next_button %} -
  • {% trans "Próximo ano" %}
  • +{% endblock %} + +{% block breadcrumbs %} + + {% if mes_pesquisa %} + {% endif %} -
  • {% trans "Exportar para PDF" %}
  • -
  • {% trans "Exportar para CSV" %}
  • -
  • {% trans "Exportar para Json" %}
  • {% endblock %} {% block content %} - {% include "eventos/alocacao_equipe_snippet.html" %} +
    + + print + + +
    + {% include "eventos/snippets/alocacao_equipe_snippet.html" with mode="html" %} + + {% if semana_pesquisa %} + {% include "eventos/snippets/calendario_modals.html" %} + {% endif %} {% endblock %} + +{% block footer %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/eventos/templates/eventos/alocacao_equipe_pdf.html b/sigi/apps/eventos/templates/eventos/alocacao_equipe_pdf.html index 8487b56..bc65af8 100644 --- a/sigi/apps/eventos/templates/eventos/alocacao_equipe_pdf.html +++ b/sigi/apps/eventos/templates/eventos/alocacao_equipe_pdf.html @@ -1,63 +1,22 @@ -{% extends "base_report.html" %} -{% load static from staticfiles %} +{% extends "pdf/base_report.html" %} +{% load static %} {% load i18n %} -{% block extra_head %} - -{% blocktrans %}Alocação de equipe em {{ ano_pesquisa }}{% endblocktrans %} {% endblock %} -{% block report %} -

    {% blocktrans %}Alocação de equipe em {{ ano_pesquisa }}{% endblocktrans %}

    - {% include "eventos/alocacao_equipe_snippet.html" %} +{% block main_content %} +

    + {% if mes_pesquisa %} + {% blocktrans with mes=mes_pesquisa|safe ano=ano_pesquisa|safe %}Alocação de equipe em {{ mes }}/{{ ano }}{% endblocktrans %}

    + {% else %} + {% blocktrans with ano=ano_pesquisa|safe %}Alocação de equipe em {{ ano }}{% endblocktrans %} + {% endif %} + {% include "eventos/snippets/alocacao_equipe_snippet.html" %} {% endblock %} - - diff --git a/sigi/apps/eventos/templates/eventos/alocacao_equipe_snippet.html b/sigi/apps/eventos/templates/eventos/alocacao_equipe_snippet.html deleted file mode 100644 index 5c67a50..0000000 --- a/sigi/apps/eventos/templates/eventos/alocacao_equipe_snippet.html +++ /dev/null @@ -1,17 +0,0 @@ -
    - - {% for linha in linhas %} - - {% if forloop.first %} - {% for coluna in linha %} - - {% endfor %} - {% else %} - {% for coluna in linha %} - - {% endfor %} - {% endif %} - - {% endfor %} -
    {{ coluna }}{{ coluna }}
    -
    diff --git a/sigi/apps/eventos/templates/eventos/snippets/alocacao_equipe_snippet.html b/sigi/apps/eventos/templates/eventos/snippets/alocacao_equipe_snippet.html new file mode 100644 index 0000000..7ca4e58 --- /dev/null +++ b/sigi/apps/eventos/templates/eventos/snippets/alocacao_equipe_snippet.html @@ -0,0 +1,44 @@ +
    + + + + {% for coluna in cabecalho %} + + {% endfor %} + + + + {% for linha in linhas %} + + {% for coluna in linha %} + {% if forloop.first %} + + {% elif forloop.last and not semana_pesquisa %} + + {% else %} + + {% endif %} + {% endfor %} + + {% endfor %} + +
    + {% if mode == "html" and not semana_pesquisa and forloop.counter0 >= 1 and forloop.counter0 <= cabecalho|length|add:-2 %} + {{ coluna }} + {% else %} + {% if semana_pesquisa and forloop.counter0 >= 1 %} + {{ coluna|date:"SHORT_DATE_FORMAT" }} + {% else %} + {{ coluna }} + {% endif %} + {% endif %} +
    {{ coluna }}{{ coluna }} + {% if semana_pesquisa %} + {% for evento in coluna %} +

    {{ evento.nome }}

    + {% endfor %} + {% else %} + {{ coluna }} + {% endif %} +
    +
    diff --git a/sigi/apps/eventos/urls.py b/sigi/apps/eventos/urls.py index 2e4efa2..d507e63 100644 --- a/sigi/apps/eventos/urls.py +++ b/sigi/apps/eventos/urls.py @@ -3,6 +3,9 @@ from sigi.apps.eventos import views urlpatterns = [ path("calendario/", views.calendario, name="eventos-calendario"), + path( + "alocacaoequipe/", views.alocacao_equipe, name="eventos-alocacao-equipe" + ), path("evento//", views.evento, name="eventos-evento"), path( "evento//convite//", diff --git a/sigi/apps/eventos/views.py b/sigi/apps/eventos/views.py index 6233526..1dd2de0 100644 --- a/sigi/apps/eventos/views.py +++ b/sigi/apps/eventos/views.py @@ -1,16 +1,24 @@ -from datetime import datetime import calendar +import csv import locale +from datetime import datetime +from functools import reduce +from typing import OrderedDict from django.contrib import messages from django.contrib.admin.sites import site from django.contrib.auth.decorators import login_required -from django.http import HttpResponse +from django.http import HttpResponse, JsonResponse from django.shortcuts import redirect, render, get_object_or_404 from django.template import Template, Context from django.template.exceptions import TemplateSyntaxError from django.utils import timezone from django.utils.text import slugify -from django.utils.translation import to_locale, get_language, gettext as _ +from django.utils.translation import ( + to_locale, + get_language, + ngettext, + gettext as _, +) from django.urls import reverse from django_weasyprint.utils import django_url_fetcher from django_weasyprint.views import WeasyTemplateResponse @@ -383,79 +391,188 @@ def gerar_anexo(casa, presidente, contato, path, modelo, nome, texto): return anexo -# @login_required -# def alocacao_equipe(request): -# ano_pesquisa = int(request.GET.get('ano', timezone.localdate().year)) -# formato = request.GET.get('fmt', 'html') - -# data = {'ano_pesquisa': ano_pesquisa} - -# if Evento.objects.filter(data_inicio__year=ano_pesquisa-1).exists(): -# data['prev_button'] = {'ano': ano_pesquisa - 1 } - -# if Evento.objects.filter(data_inicio__year=ano_pesquisa+1).exists(): -# data['next_button'] = {'ano': ano_pesquisa + 1 } - -# dados = [] - -# for evento in Evento.objects.filter(data_inicio__year=ano_pesquisa).exclude(status='C').prefetch_related('equipe_set'): -# for p in evento.equipe_set.all(): -# registro = None -# for r in dados: -# if r[0] == p.membro.pk: -# registro = r -# break -# if not registro: -# registro = [p.membro.pk, p.membro.nome_completo, [{'dias': 0, 'eventos': 0} for x in range(1,13)]] -# dados.append(registro) - -# registro[2][evento.data_inicio.month-1]['dias'] += (evento.data_termino - evento.data_inicio).days + 1 -# registro[2][evento.data_inicio.month-1]['eventos'] += 1 - -# dados.sort(lambda x, y: cmp(x[1], y[1])) - -# lang = (translation.to_locale(translation.get_language())+'.utf8').encode() -# locale.setlocale(locale.LC_ALL, lang) -# meses = [calendar.month_name[m] for m in range(1,13)] - -# linhas = [[_("Servidor")] + meses + ['total']] - -# for r in dados: -# r[2].append(reduce(lambda x,y:{'dias': x['dias'] + y['dias'], -# 'eventos': x['eventos'] + y['eventos']}, r[2])) -# linhas.append([r[1]] + -# [_(ungettext("%(dias)s dia", "%(dias)s dias", d['dias']) + " em " + -# ungettext("%(eventos)s evento", "%(eventos)s eventos", d['eventos']) -# ) % d if d['dias'] > 0 or d['eventos'] > 0 else '' for d in r[2]]) - -# # for registro in Servidor.objects.filter(equipe_evento__evento__data_inicio__year=ano_pesquisa).exclude(equipe_evento__evento__status='C').distinct(): -# # dados = [{'dias': 0, 'eventos': 0} for x in range(1,13)] -# # for part in registro.equipe_evento.filter(evento__data_inicio__year=ano_pesquisa).exclude(evento__status='C'): -# # dados[part.evento.data_inicio.month-1]['dias'] += (part.evento.data_termino - -# # part.evento.data_inicio).days + 1 -# # dados[part.evento.data_inicio.month-1]['eventos'] += 1 -# # dados.append([registro.nome_completo] + [_(ungettext("%(dias)s dia", "%(dias)s dias", d['dias']) + " em " + ungettext("%(eventos)s evento", "%(eventos)s eventos", d['eventos'])) % d if d['dias'] > 0 or d['eventos'] > 0 else '' for d in dados]) - -# data['linhas'] = linhas - -# if formato == 'pdf': -# return render_to_pdf('eventos/alocacao_equipe_pdf.html', data) -# elif formato == 'csv': -# response = HttpResponse(content_type='text/csv') -# response['Content-Disposition'] = 'attachment; filename="alocacao_equipe_%s.csv"' % (ano_pesquisa,) -# writer = csv.writer(response) -# asc_list = [[s.encode('utf-8') if isinstance(s, unicode) else s for s in l] for l in linhas] -# writer.writerows(asc_list) -# return response -# elif formato == 'json': -# result = {'ano': ano_pesquisa, -# 'equipe': [{'pk': d[0], -# 'nome_completo': d[1], -# 'meses': {m[0]: m[1] for m in zip(meses+['total'], d[2])} -# } for d in dados]} -# return JsonResponse(result) - -# return render(request, 'eventos/alocacao_equipe.html', data) +@login_required +def alocacao_equipe(request): + ano_pesquisa = int(request.GET.get("ano", timezone.localdate().year)) + mes_pesquisa = int(request.GET.get("mes", 0)) + semana_pesquisa = int(request.GET.get("semana", 0)) + formato = request.GET.get("fmt", "html") + + lang = to_locale(get_language()) + ".UTF-8" + locale.setlocale(locale.LC_ALL, lang) + + dados = [] + eventos = Evento.objects.exclude(status="C").prefetch_related("equipe_set") + + num_cols = 12 + + if mes_pesquisa > 0: + semanas = [ + [s[0], s[-1]] + for s in calendar.Calendar().monthdatescalendar( + ano_pesquisa, mes_pesquisa + ) + ] + num_cols = len(semanas) + if semana_pesquisa > 0: + dias = calendar.Calendar().monthdatescalendar( + ano_pesquisa, mes_pesquisa + )[semana_pesquisa - 1] + num_cols = len(dias) + eventos = eventos.filter( + data_inicio__gte=dias[0], data_inicio__lte=dias[-1] + ) + else: + eventos = eventos.filter( + data_inicio__gte=semanas[0][0], data_inicio__lte=semanas[-1][-1] + ) + else: + eventos = eventos.filter(data_inicio__year=ano_pesquisa) + + for evento in eventos: + for p in evento.equipe_set.all(): + registro = None + for r in dados: + if r[0] == p.membro.pk: + registro = r + break + if not registro: + if semana_pesquisa > 0: + registro = [ + p.membro.pk, + p.membro.nome_completo, + OrderedDict([(dia, []) for dia in dias]), + ] + else: + registro = [ + p.membro.pk, + p.membro.nome_completo, + [{"dias": 0, "eventos": 0} for __ in range(num_cols)], + ] + dados.append(registro) + + if mes_pesquisa > 0: + if semana_pesquisa > 0: + for dia in dias: + if ( + evento.data_inicio.date() + <= dia + <= evento.data_termino.date() + ): + registro[2][dia].append(evento) + else: + for idx, [inicio, fim] in enumerate(semanas): + if inicio <= evento.data_inicio.date() <= fim: + registro[2][idx]["dias"] += ( + min(fim, evento.data_termino.date()) + - evento.data_inicio.date() + ).days + 1 + registro[2][idx]["eventos"] += 1 + elif inicio <= evento.data_termino.date() <= fim: + registro[2][idx]["dias"] += ( + min(fim, evento.data_termino.date()) + - evento.data_inicio.date() + ).days + 1 + registro[2][idx]["eventos"] += 1 + else: + registro[2][evento.data_inicio.month - 1]["dias"] += ( + evento.data_termino - evento.data_inicio + ).days + 1 + registro[2][evento.data_inicio.month - 1]["eventos"] += 1 + + dados.sort(key=lambda x: x[1]) + + meses = list(calendar.month_abbr)[1:] + linhas = [] + + if semana_pesquisa: + linhas = [ + [registro[1]] + list(registro[2].values()) for registro in dados + ] + else: + for r in dados: + r[2].append( + reduce( + lambda x, y: { + "dias": x["dias"] + y["dias"], + "eventos": x["eventos"] + y["eventos"], + }, + r[2], + ) + ) + linhas.append( + [r[1]] + + [ + _( + ngettext("%(dias)s dia", "%(dias)s dias", d["dias"]) + + " em " + + ngettext( + "%(eventos)s evento", + "%(eventos)s eventos", + d["eventos"], + ) + ) + % d + if d["dias"] > 0 or d["eventos"] > 0 + else "" + for d in r[2] + ] + ) + + context = site.each_context(request) or {} + context.update( + { + "anos": Evento.objects.exclude(data_inicio=None) + .order_by("data_inicio__year") + .distinct("data_inicio__year") + .values_list("data_inicio__year", flat=True), + "ano_pesquisa": ano_pesquisa, + "linhas": linhas, + } + ) + if mes_pesquisa > 0: + context["mes_pesquisa"] = mes_pesquisa + context["meses"] = meses + if semana_pesquisa > 0: + cabecalho = [_("Servidor")] + dias + context["semana_pesquisa"] = semana_pesquisa + context["eventos"] = eventos + else: + cabecalho = ( + [_("Servidor")] + + [ + _(f"de {inicio:%d/%m} a {fim:%d/%m}") + for inicio, fim in semanas + ] + + ["total"] + ) + else: + cabecalho = [_("Servidor")] + meses + ["total"] + + context["cabecalho"] = cabecalho + + if formato == "pdf": + context["title"] = _("Alocação de equipe") + context["pdf"] = True + return WeasyTemplateResponse( + # filename="alocacao_equipe.pdf", + request=request, + template="eventos/alocacao_equipe_pdf.html", + context=context, + content_type="application/pdf", + ) + elif formato == "csv": + response = HttpResponse(content_type="text/csv") + response[ + "Content-Disposition" + ] = 'attachment; filename="alocacao_equipe_%s.csv"' % (ano_pesquisa,) + writer = csv.writer(response) + writer.writerow(cabecalho) + writer.writerows(linhas) + return response + + return render(request, "eventos/alocacao_equipe.html", context) + # # Views e functions para carrinho de exportação diff --git a/sigi/menu_conf.yaml b/sigi/menu_conf.yaml index 0125cc5..81fa240 100644 --- a/sigi/menu_conf.yaml +++ b/sigi/menu_conf.yaml @@ -86,7 +86,7 @@ main_menu: - title: Calendário mensal view_name: eventos-calendario - title: Alocação de equipe - view_name: + view_name: eventos-alocacao-equipe - title: Servidores icon: account_circle children: