From 8e2846b407227092d921169ba5a528b8304dc833 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 9 Jul 2020 10:17:40 +0200
Subject: [PATCH 4/5] manager: add opened events view for event agenda (#44457)

---
 chrono/agendas/models.py                      |  2 +-
 chrono/api/views.py                           |  4 +-
 chrono/manager/static/css/style.scss          |  4 ++
 .../chrono/manager_agenda_open_events.html    | 37 +++++++++++
 .../templates/chrono/manager_agenda_view.html |  3 +-
 .../manager_events_agenda_month_view.html     |  4 ++
 chrono/manager/urls.py                        |  5 ++
 chrono/manager/views.py                       | 20 ++++++
 tests/test_api.py                             |  6 +-
 tests/test_manager.py                         | 64 +++++++++++++++++++
 10 files changed, 142 insertions(+), 7 deletions(-)
 create mode 100644 chrono/manager/templates/chrono/manager_agenda_open_events.html

diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py
index 21091f2..5586d48 100644
--- a/chrono/agendas/models.py
+++ b/chrono/agendas/models.py
@@ -405,7 +405,7 @@ class Agenda(models.Model):
                 for weektime_interval in IntervalSet.simple(*time_period_interval) - closed_hours_by_days:
                     yield SharedTimePeriod.from_weektime_interval(weektime_interval, desks=desks)
 
-    def get_opened_events(self):
+    def get_open_events(self):
         assert self.kind == 'events'
 
         entries = self.event_set.all()
diff --git a/chrono/api/views.py b/chrono/api/views.py
index e7d3b7a..38e1d39 100644
--- a/chrono/api/views.py
+++ b/chrono/api/views.py
@@ -294,7 +294,7 @@ def get_agenda_detail(request, agenda, check_events=False):
             )
         }
         if check_events:
-            agenda_detail['opened_events_available'] = agenda.get_opened_events().filter(full=False).exists()
+            agenda_detail['opened_events_available'] = agenda.get_open_events().filter(full=False).exists()
     elif agenda.accept_meetings():
         agenda_detail['api'] = {
             'meetings_url': request.build_absolute_uri(
@@ -393,7 +393,7 @@ class Datetimes(APIView):
         if agenda.kind != 'events':
             raise Http404('agenda found, but it was not an events agenda')
 
-        entries = agenda.get_opened_events()
+        entries = agenda.get_open_events()
 
         if 'date_start' in request.GET:
             entries = entries.filter(
diff --git a/chrono/manager/static/css/style.scss b/chrono/manager/static/css/style.scss
index 0b765dd..68e6afc 100644
--- a/chrono/manager/static/css/style.scss
+++ b/chrono/manager/static/css/style.scss
@@ -95,6 +95,10 @@ a.timeperiod-exception-all {
 	min-width: 16ex;
 }
 
+.openevents .section h4:first-child {
+	margin-top: 0;
+}
+
 .agenda-table thead th {
 	width: 14vw;
 	padding-bottom: 1ex;
diff --git a/chrono/manager/templates/chrono/manager_agenda_open_events.html b/chrono/manager/templates/chrono/manager_agenda_open_events.html
new file mode 100644
index 0000000..da4da8f
--- /dev/null
+++ b/chrono/manager/templates/chrono/manager_agenda_open_events.html
@@ -0,0 +1,37 @@
+{% extends "chrono/manager_agenda_view.html" %}
+{% load i18n %}
+
+{% block bodyargs %}class="openevents"{% endblock %}
+
+{% block breadcrumb %}
+{{ block.super }}
+<a href="{% url 'chrono-manager-agenda-open-events-view' pk=agenda.pk %}">{% trans "Open events" %}</a>
+{% endblock %}
+
+{% block extra-actions %}
+<a href="{% url 'chrono-manager-agenda-month-redirect-view' pk=agenda.pk %}">{% trans 'Month view' %}</a>
+{% endblock %}
+
+{% block content %}
+<div class="section">
+<h3>{% trans "Open events" %}</h3>
+<div>
+{% if open_events %}
+  {% for event in open_events %}
+    {% ifchanged event.start_datetime.month event.start_datetime.year %}
+      <h4>{{ event.start_datetime|date:'YEAR_MONTH_FORMAT'|capfirst }}</h4>
+      <ul class="objects-list single-links">
+    {% endifchanged %}
+    {% include 'chrono/manager_agenda_event_fragment.html' %}
+    {% if forloop.last %}</ul>{% endif %}
+  {% endfor %}
+{% else %}
+<div class="big-msg-info">
+  {% blocktrans %}
+  This agenda doesn't have any open event configured.
+  {% endblocktrans %}
+</div>
+{% endif %}
+</div>
+</div>
+{% endblock %}
diff --git a/chrono/manager/templates/chrono/manager_agenda_view.html b/chrono/manager/templates/chrono/manager_agenda_view.html
index 9a2c62a..487a293 100644
--- a/chrono/manager/templates/chrono/manager_agenda_view.html
+++ b/chrono/manager/templates/chrono/manager_agenda_view.html
@@ -9,7 +9,8 @@
 <h2>{{ object.label }}</h2>
 {% if user_can_manage %}
   <span class="actions">
-  <a href="{% url 'chrono-manager-agenda-settings' pk=object.id %}">{% trans 'Settings' %}</a>
+    <a href="{% url 'chrono-manager-agenda-settings' pk=object.id %}">{% trans 'Settings' %}</a>
+    {% block extra-actions %}{% endblock %}
   </span>
 {% endif %}
 {% endblock %}
diff --git a/chrono/manager/templates/chrono/manager_events_agenda_month_view.html b/chrono/manager/templates/chrono/manager_events_agenda_month_view.html
index 1a45db7..2132e95 100644
--- a/chrono/manager/templates/chrono/manager_events_agenda_month_view.html
+++ b/chrono/manager/templates/chrono/manager_events_agenda_month_view.html
@@ -1,6 +1,10 @@
 {% extends "chrono/manager_agenda_month_view.html" %}
 {% load i18n %}
 
+{% block extra-actions %}
+<a href="{% url 'chrono-manager-agenda-open-events-view' pk=agenda.pk %}">{% trans 'Open events' %}</a>
+{% endblock %}
+
 {% block content %}
 <div class="section">
 <h3>{% trans "Events" %}</h3>
diff --git a/chrono/manager/urls.py b/chrono/manager/urls.py
index d5e7b3e..fd2e6ab 100644
--- a/chrono/manager/urls.py
+++ b/chrono/manager/urls.py
@@ -53,6 +53,11 @@ urlpatterns = [
         views.agenda_day_view,
         name='chrono-manager-agenda-day-view',
     ),
+    url(
+        r'^agendas/(?P<pk>\d+)/events/open/$',
+        views.agenda_open_events_view,
+        name='chrono-manager-agenda-open-events-view',
+    ),
     url(r'^agendas/(?P<pk>\d+)/settings$', views.agenda_settings, name='chrono-manager-agenda-settings'),
     url(r'^agendas/(?P<pk>\d+)/edit$', views.agenda_edit, name='chrono-manager-agenda-edit'),
     url(r'^agendas/(?P<pk>\d+)/delete$', views.agenda_delete, name='chrono-manager-agenda-delete'),
diff --git a/chrono/manager/views.py b/chrono/manager/views.py
index a15d232..a996c20 100644
--- a/chrono/manager/views.py
+++ b/chrono/manager/views.py
@@ -1019,6 +1019,26 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
 agenda_monthly_view = AgendaMonthView.as_view()
 
 
+class AgendaOpenEventsView(ManagedAgendaMixin, DetailView):
+    model = Agenda
+    template_name = 'chrono/manager_agenda_open_events.html'
+
+    def set_agenda(self, **kwargs):
+        self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events')
+
+    def get_object(self, **kwargs):
+        return self.agenda
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context['user_can_manage'] = self.agenda.can_be_managed(self.request.user)
+        context['open_events'] = Event.annotate_queryset(self.agenda.get_open_events())
+        return context
+
+
+agenda_open_events_view = AgendaOpenEventsView.as_view()
+
+
 class ManagedAgendaSubobjectMixin(object):
     agenda = None
 
diff --git a/tests/test_api.py b/tests/test_api.py
index b6db0aa..5d5f734 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -2419,7 +2419,7 @@ def test_agenda_detail_api(app):
     agenda.minimal_booking_delay = 10
     agenda.save()
     resp = app.get('/api/agenda/%s/' % agenda.slug)
-    assert list(agenda.get_opened_events()) == [event2, event3]
+    assert list(agenda.get_open_events()) == [event2, event3]
     assert resp.json['data']['opened_events_available'] is False
 
     # event3 is not full but too late
@@ -2427,14 +2427,14 @@ def test_agenda_detail_api(app):
     agenda.maximal_booking_delay = 12
     agenda.save()
     resp = app.get('/api/agenda/%s/' % agenda.slug)
-    assert list(agenda.get_opened_events()) == [event2]
+    assert list(agenda.get_open_events()) == [event2]
     assert resp.json['data']['opened_events_available'] is False
 
     # events are not full but not published
     Event.objects.update(full=False)
     agenda.event_set.update(publication_date=now().date() + datetime.timedelta(days=20))
     resp = app.get('/api/agenda/%s/' % agenda.slug)
-    assert list(agenda.get_opened_events()) == []
+    assert list(agenda.get_open_events()) == []
     assert resp.json['data']['opened_events_available'] is False
 
     # unknown
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 244baab..3afc489 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -2499,6 +2499,70 @@ def test_agenda_events_month_view(app, admin_user):
     assert "This month doesn't have any event configured." in resp.text
 
 
+def test_agenda_open_events_view(app, admin_user):
+    agenda = Agenda.objects.create(
+        label='Events', kind='events', minimal_booking_delay=2, maximal_booking_delay=5
+    )
+    today = datetime.date.today()
+
+    login(app)
+    resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.pk, today.year, today.month))
+    assert 'Open events' in resp.text
+
+    resp = app.get('/manage/agendas/%s/events/open/' % agenda.pk)
+    assert 'Month view' in resp.text
+    assert "This agenda doesn't have any open event configured." in resp.text
+
+    # create some events
+    # past event
+    Event.objects.create(
+        agenda=agenda, label='event A', start_datetime=now() - datetime.timedelta(days=1), places=42
+    )
+    # too late
+    Event.objects.create(
+        agenda=agenda, label='event B', start_datetime=now() + datetime.timedelta(days=1), places=42
+    )
+    # too soon
+    Event.objects.create(
+        agenda=agenda, label='event C', start_datetime=now() + datetime.timedelta(days=6), places=42
+    )
+    # in range
+    Event.objects.create(
+        agenda=agenda, label='event D', start_datetime=now() + datetime.timedelta(days=3), places=42
+    )
+    # publication date in future
+    Event.objects.create(
+        agenda=agenda,
+        label='event E',
+        start_datetime=now() + datetime.timedelta(days=3),
+        publication_date=today + datetime.timedelta(days=1),
+        places=42,
+    )
+    # publication date in past
+    Event.objects.create(
+        agenda=agenda,
+        label='event F',
+        start_datetime=now() + datetime.timedelta(days=3),
+        publication_date=today - datetime.timedelta(days=1),
+        places=42,
+    )
+    resp = app.get('/manage/agendas/%s/events/open/' % agenda.pk)
+    assert 'event A' not in resp.text
+    assert 'event B' not in resp.text
+    assert 'event C' not in resp.text
+    assert 'event D' in resp.text
+    assert 'event E' not in resp.text
+    assert 'event F' in resp.text
+
+    # wrong kind
+    agenda.kind = 'meetings'
+    agenda.save()
+    resp = app.get('/manage/agendas/%s/events/open/' % agenda.pk, status=404)
+    agenda.kind = 'virtual'
+    agenda.save()
+    resp = app.get('/manage/agendas/%s/events/open/' % agenda.pk, status=404)
+
+
 def test_agenda_month_view(app, admin_user, manager_user, api_user):
     agenda = Agenda.objects.create(label='Passeports', kind='meetings')
     desk = Desk.objects.create(agenda=agenda, label='Desk A')
-- 
2.20.1

