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 %}
+
+
+
+
+ {% 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" %} |
+
+
+
+ {% for reserva in reservas %}
+
+ {% ifchanged reserva.espaco %}
+ {{ reserva.espaco.nome }} |
+ {% endifchanged %}
+ {{ 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 %}
+
+ |
+
+ {% 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 %}
+
+
+
+
+
+ {% 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 @@