diff --git a/sigi/apps/espacos/admin_urls.py b/sigi/apps/espacos/admin_urls.py index d997a6a..89e65ab 100644 --- a/sigi/apps/espacos/admin_urls.py +++ b/sigi/apps/espacos/admin_urls.py @@ -3,4 +3,5 @@ from sigi.apps.espacos import views urlpatterns = [ path("agenda/", views.Agenda.as_view(), name="espacos_agenda"), + path("usoespacos/", views.UsoEspacos.as_view(), name="espacos_usoespaco"), ] diff --git a/sigi/apps/espacos/forms.py b/sigi/apps/espacos/forms.py new file mode 100644 index 0000000..7d2e8d2 --- /dev/null +++ b/sigi/apps/espacos/forms.py @@ -0,0 +1,44 @@ +import calendar +from django.utils import timezone +from django import forms +from material.admin.widgets import MaterialAdminDateWidget +from django.forms.widgets import CheckboxSelectMultiple +from django.utils.translation import gettext as _ +from sigi.apps.espacos.models import Espaco + + +class UsoEspacoReportForm(forms.Form): + class Media: + css = {"all": ["css/change_form.css"]} + js = [ + "/admin/js/vendor/select2/select2.full.js", + "/admin/js/change_form.js", + "/admin/js/vendor/select2/i18n/pt-BR.js", + "/material/admin/js/widgets/TimeInput.js", + "/admin/js/core.js", + ] + + def get_semana(self): + return [ + {"first": s[0], "last": s[-1]} + for s in calendar.Calendar().monthdatescalendar( + timezone.localdate().year, timezone.localdate().month + ) + if s[0] <= timezone.localdate() <= s[-1] + ][0] + + data_inicio = forms.DateField( + label=_("Data início"), required=True, widget=MaterialAdminDateWidget + ) + data_fim = forms.DateField( + label=_("Data fim"), required=True, widget=MaterialAdminDateWidget + ) + espaco = forms.ModelMultipleChoiceField( + label=_("Espaços"), required=True, queryset=Espaco.objects.all(), widget=CheckboxSelectMultiple + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + semana = self.get_semana() + self.fields["data_inicio"].initial = semana["first"] + self.fields["data_fim"].initial = semana["last"] diff --git a/sigi/apps/espacos/templates/espacos/agenda_pdf.html b/sigi/apps/espacos/templates/espacos/agenda_pdf.html index b9c661d..6325afd 100644 --- a/sigi/apps/espacos/templates/espacos/agenda_pdf.html +++ b/sigi/apps/espacos/templates/espacos/agenda_pdf.html @@ -1,8 +1,6 @@ {% extends 'pdf/base_report.html' %} {% load static i18n sigi_tags %} -{% block page_size %}A4 landscape{% endblock page_size %} - {% block extra_style %} {{ block.super }} blockquote { diff --git a/sigi/apps/espacos/templates/espacos/snippets/uso_espaco_snippet.html b/sigi/apps/espacos/templates/espacos/snippets/uso_espaco_snippet.html new file mode 100644 index 0000000..3f21eb3 --- /dev/null +++ b/sigi/apps/espacos/templates/espacos/snippets/uso_espaco_snippet.html @@ -0,0 +1,43 @@ +{% load i18n static sigi_tags dict_get %} + + + + + + + + + + + + + + + + + + + + {% for reserva in reservas %} + + {% ifchanged reserva.espaco %} + + {% endifchanged %} + + + + + + + + + + {% endfor %} + +
{% trans "Espaço" %}{% trans "Data início" %}{% trans "Data término" %}{% trans "Propósito" %}{% trans "Solicitante" %}{% trans "Contato" %}{% trans "Informações adicionais" %}{% trans "Recursos solicitados" %}
{% trans "Nome" %}{% trans "Telefone" %}
{{ reserva.espaco.nome }}{{ reserva.inicio }}{{ reserva.termino }}{{ reserva.proposito }}{{ reserva.solicitante }}{{ reserva.contato }}{{ reserva.telefone_contato }}{{ reserva.informacoes }} +
    + {% for recurso in reserva.recursosolicitado_set.all %} +
  • {{ recurso.quantidade}} {{ recurso.recurso }} ( {{ recurso.observacoes }})
  • + {% endfor %} +
+
\ No newline at end of file diff --git a/sigi/apps/espacos/templates/espacos/uso_espaco.html b/sigi/apps/espacos/templates/espacos/uso_espaco.html new file mode 100644 index 0000000..c898fab --- /dev/null +++ b/sigi/apps/espacos/templates/espacos/uso_espaco.html @@ -0,0 +1,56 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %} + {{ block.super }} + + +{% endblock %} + +{% block breadcrumbs %}{% endblock %} +{% block coltype %}colMS{% endblock %} + +{% block content %} +
+
+
+
+
+ {{ form }} +
+
+ + +
+
+
+
+
+
+
+
+
+ {% include "espacos/snippets/uso_espaco_snippet.html" %} +
+
+
+
+{% endblock %} + +{% block footer %} + {{ block.super }} + {{ form.media }} +{% endblock %} diff --git a/sigi/apps/espacos/templates/espacos/uso_espaco_pdf.html b/sigi/apps/espacos/templates/espacos/uso_espaco_pdf.html new file mode 100644 index 0000000..04d7379 --- /dev/null +++ b/sigi/apps/espacos/templates/espacos/uso_espaco_pdf.html @@ -0,0 +1,19 @@ +{% extends "pdf/base_report.html" %} +{% load i18n static %} + +{% block extra_style %} + {{ block.super }} + h4 { + margin: 20px 0; + padding-left: 1.5rem; + border-left: 5px solid #ee6e73; + } +{% endblock %} + +{% block main_content %} +

+ {% blocktranslate%}Semana de {{ data_inicio }} a {{ data_termino }}{% endblocktranslate %} +

+
+ {% include "espacos/snippets/uso_espaco_snippet.html" %} +{% endblock %} \ No newline at end of file diff --git a/sigi/apps/espacos/views.py b/sigi/apps/espacos/views.py index 3849990..83ea050 100644 --- a/sigi/apps/espacos/views.py +++ b/sigi/apps/espacos/views.py @@ -2,29 +2,24 @@ import calendar import locale from typing import Any from django import http -from django.db.models import Q -from django.template.response import TemplateResponse +from django.db.models import Q, Count from django.utils import timezone from django.utils.translation import ( to_locale, get_language, - ngettext, gettext as _, ) from django.views.generic.base import TemplateView -from django_weasyprint.views import WeasyTemplateResponse from sigi.apps.espacos.models import Espaco, Reserva +from sigi.apps.espacos.forms import UsoEspacoReportForm +from sigi.apps.utils.mixins import ReportViewMixin, StaffMemberRequiredMixin -class Agenda(TemplateView): - def _is_pdf(self): - return bool(self.request.GET.get("pdf", 0)) - - def get_template_names(self): - if self._is_pdf(): - return ["espacos/agenda_pdf.html"] - else: - return ["espacos/agenda.html"] +class Agenda(ReportViewMixin, StaffMemberRequiredMixin, TemplateView): + html_template_name = "espacos/agenda.html" + pdf_template_name = "espacos/agenda_pdf.html" + report_title = _("Reserva de espaços do ILB") + pagesize = "A4 landscape" def get_context_data(self, **kwargs): mes_pesquisa = int( @@ -108,19 +103,59 @@ class Agenda(TemplateView): context["semanas"] = semanas context["day_names"] = calendar.day_abbr - if self._is_pdf(): - context["pdf"] = True - context["title"] = _("Reserva de espaços do ILB") - return context - def render_to_response(self, context, **response_kwargs): - self.response_class = TemplateResponse - self.content_type = None - if self._is_pdf(): - self.content_type = "application/pdf" - self.response_class = WeasyTemplateResponse - response_kwargs.setdefault( - "filename", f"agenda-{timezone.localdate()}.pdf" + +class UsoEspacos(ReportViewMixin, StaffMemberRequiredMixin, TemplateView): + html_template_name = "espacos/uso_espaco.html" + pdf_template_name = "espacos/uso_espaco_pdf.html" + report_title = _("Uso dos espaços") + pagesize = "A4 landscape" + + def get_context_data(self, **kwargs): + form = UsoEspacoReportForm(self.request.GET) + if form.is_valid(): + data_inicio = form.cleaned_data["data_inicio"] + data_fim = form.cleaned_data["data_fim"] + sel_espacos = form.cleaned_data["espaco"] + else: + form = UsoEspacoReportForm( + initial={"espaco": Espaco.objects.all()} + ) + semana = form.get_semana() + data_inicio = semana["first"] + data_fim = semana["last"] + sel_espacos = None + + if not sel_espacos: + sel_espacos = Espaco.objects.all() + + reservas = ( + Reserva.objects.filter( + status=Reserva.STATUS_ATIVO, espaco__in=sel_espacos + ) + .filter( + Q(inicio__range=(data_inicio, data_fim)) + | Q(termino__range=(data_inicio, data_fim)) ) - return super().render_to_response(context, **response_kwargs) + .order_by("espaco", "inicio", "termino") + ) + rowspans = dict( + reservas.order_by("espaco") + .values_list("espaco") + .annotate(Count("espaco")) + ) + + context = super().get_context_data(**kwargs) + context.update( + { + "reservas": reservas, + "rowspans": rowspans, + "form": form, + "data_inicio": data_inicio, + "data_termino": data_fim, + "sel_espacos": sel_espacos, + } + ) + + return context diff --git a/sigi/apps/utils/mixins.py b/sigi/apps/utils/mixins.py index 5d4b999..00cc262 100644 --- a/sigi/apps/utils/mixins.py +++ b/sigi/apps/utils/mixins.py @@ -5,12 +5,15 @@ from django.contrib import admin from django.contrib.admin import helpers from django.contrib.admin.options import csrf_protect_m from django.contrib.admin.utils import pretty_name +from django.contrib.auth.mixins import UserPassesTestMixin from django.core.exceptions import PermissionDenied, ImproperlyConfigured from django.http import Http404 from django.http.response import HttpResponse, HttpResponseRedirect from django.template.response import TemplateResponse from django.urls import path +from django.utils import timezone from django.utils.translation import gettext as _, ngettext +from django_weasyprint.views import WeasyTemplateResponse from import_export import resources from import_export.admin import ImportMixin, ExportMixin from import_export.fields import Field @@ -308,13 +311,63 @@ class ReturnMixin: return HttpResponseRedirect(self._return_path) return response + class AsciifyQParameter: def asciify_q_param(self, request): if "q" in request.GET: request.GET._mutable = True request.GET["q"] = to_ascii(request.GET["q"]) request.GET._mutable = False - + def get_queryset(self, request): self.asciify_q_param(request) return super().get_queryset(request) + + +class StaffMemberRequiredMixin(UserPassesTestMixin): + def test_func(self): + return self.request.user.is_staff + + +class ReportViewMixin: + html_template_name = None + pdf_template_name = None + report_title = _("Report") + pagesize = None + attachment = True + + def _is_pdf(self): + return bool(self.request.GET.get("pdf", 0)) + + def get_template_names(self): + if self.html_template_name is None or self.pdf_template_name is None: + raise ImproperlyConfigured( + "TemplateResponseMixin requires either a definition of " + "'html_template_name' and 'pdf_template_name' or an " + "implementation of 'get_template_names()'" + ) + if self._is_pdf(): + return [self.pdf_template_name] + else: + return [self.html_template_name] + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["title"] = self.report_title + context["pdf"] = self._is_pdf() + if self.pagesize: + context["pagesize"] = self.pagesize + return context + + def render_to_response(self, context, **response_kwargs): + self.response_class = TemplateResponse + self.content_type = None + if self._is_pdf(): + self.content_type = "application/pdf" + self.response_class = WeasyTemplateResponse + response_kwargs.setdefault( + "filename", + f"{self.report_title.lower()}-{timezone.localdate()}.pdf", + ) + response_kwargs.setdefault("attachment", self.attachment) + return super().render_to_response(context, **response_kwargs) diff --git a/sigi/menu_conf.yaml b/sigi/menu_conf.yaml index 8866a10..308e5e4 100644 --- a/sigi/menu_conf.yaml +++ b/sigi/menu_conf.yaml @@ -79,6 +79,8 @@ main_menu: querystr: status=A - title: Agenda de reservas view_name: espacos_agenda + - title: Uso dos espaços + view_name: espacos_usoespaco - title: Eventos icon: school children: diff --git a/sigi/templates/pdf/base.html b/sigi/templates/pdf/base.html index 485e06c..cfcbe21 100644 --- a/sigi/templates/pdf/base.html +++ b/sigi/templates/pdf/base.html @@ -7,7 +7,7 @@