From b5c2fbd9dc308a97a8868fbb4b3a5bcd4f150af5 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Thu, 28 May 2020 16:37:01 +0200
Subject: [PATCH 1/2] manager: add duration on events (#37352)

---
 .../agendas/migrations/0045_event_duration.py | 24 +++++++++++++++++++
 chrono/agendas/models.py                      | 11 +++++++--
 tests/test_manager.py                         | 19 +++++++++++++++
 3 files changed, 52 insertions(+), 2 deletions(-)
 create mode 100644 chrono/agendas/migrations/0045_event_duration.py

diff --git a/chrono/agendas/migrations/0045_event_duration.py b/chrono/agendas/migrations/0045_event_duration.py
new file mode 100644
index 0000000..41c395a
--- /dev/null
+++ b/chrono/agendas/migrations/0045_event_duration.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.18 on 2020-05-30 13:13
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('agendas', '0044_event_publication_date'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='event',
+            options={'ordering': ['agenda', 'start_datetime', 'duration', 'label']},
+        ),
+        migrations.AddField(
+            model_name='event',
+            name='duration',
+            field=models.PositiveIntegerField(blank=True, default=None, null=True, verbose_name='Duration (in minutes)'),
+        ),
+    ]
diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py
index 6bacab4..1304a33 100644
--- a/chrono/agendas/models.py
+++ b/chrono/agendas/models.py
@@ -633,16 +633,17 @@ class MeetingType(models.Model):
             'slug': self.slug,
             'duration': self.duration,
         }
 
 
 class Event(models.Model):
     agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE)
     start_datetime = models.DateTimeField(_('Date/time'))
+    duration = models.PositiveIntegerField(_('Duration (in minutes)'), default=None, null=True, blank=True)
     publication_date = models.DateField(_('Publication date'), blank=True, null=True)
     places = models.PositiveIntegerField(_('Places'))
     waiting_list_places = models.PositiveIntegerField(_('Places in waiting list'), default=0)
     label = models.CharField(
         _('Label'),
         max_length=150,
         null=True,
         blank=True,
@@ -654,17 +655,17 @@ class Event(models.Model):
     )
     pricing = models.CharField(_('Pricing'), max_length=150, null=True, blank=True)
     url = models.CharField(_('URL'), max_length=200, null=True, blank=True)
     full = models.BooleanField(default=False)
     meeting_type = models.ForeignKey(MeetingType, null=True, on_delete=models.CASCADE)
     desk = models.ForeignKey('Desk', null=True, on_delete=models.CASCADE)
 
     class Meta:
-        ordering = ['agenda', 'start_datetime', 'label']
+        ordering = ['agenda', 'start_datetime', 'duration', 'label']
         unique_together = ('agenda', 'slug')
 
     def __str__(self):
         if self.label:
             return self.label
         return date_format(localtime(self.start_datetime), format='DATETIME_FORMAT')
 
     def save(self, *args, **kwargs):
@@ -741,17 +742,23 @@ class Event(models.Model):
         return self.booking_set.filter(cancellation_datetime__isnull=True, in_waiting_list=False).count()
 
     @property
     def waiting_list(self):
         return self.booking_set.filter(cancellation_datetime__isnull=True, in_waiting_list=True).count()
 
     @property
     def end_datetime(self):
-        return self.start_datetime + datetime.timedelta(minutes=self.meeting_type.duration)
+        if self.meeting_type:
+            minutes = self.meeting_type.duration
+        else:
+            minutes = self.duration
+        if minutes is None:
+            return None
+        return self.start_datetime + datetime.timedelta(minutes=minutes)
 
     def get_absolute_url(self):
         return reverse('chrono-manager-event-edit', kwargs={'pk': self.agenda.id, 'event_pk': self.id})
 
     @classmethod
     def import_json(cls, data):
         data['start_datetime'] = make_aware(
             datetime.datetime.strptime(data['start_datetime'], '%Y-%m-%d %H:%M:%S')
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 1fb5faf..50bfdeb 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -457,40 +457,59 @@ def test_add_event_as_manager(app, manager_user):
     resp.form['places'] = 10
     resp = resp.form.submit()
     resp = resp.follow()
     event = Event.objects.get(places=10)
     assert "This agenda doesn't have any event yet." not in resp.text
     assert '/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id) in resp.text
     assert 'Feb. 15, 2016, 5 p.m.' in resp.text
     assert '10 places' in resp.text
+    assert event.duration is None
+    assert event.end_datetime is None
+
+    resp = resp.click('New Event')
+    resp.form['start_datetime$date'] = '2016-02-15'
+    resp.form['start_datetime$time'] = '17:00'
+    resp.form['duration'] = 45
+    resp.form['places'] = 12
+    resp = resp.form.submit()
+    resp = resp.follow()
+    event = Event.objects.get(places=12)
+    assert event.duration == 45
+    assert event.end_datetime == event.start_datetime + datetime.timedelta(minutes=45)
 
 
 def test_edit_event(app, admin_user):
     agenda = Agenda(label=u'Foo bar')
     agenda.save()
     event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=20, agenda=agenda)
     event.save()
+    assert event.duration is None
+    assert event.end_datetime is None
     app = login(app)
     resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
     resp = resp.click('Feb. 15, 2016, 5 p.m.')
     assert resp.form['start_datetime$date'].value == '2016-02-15'
     assert resp.form['start_datetime$time'].value == '17:00'
     assert resp.form['publication_date'].value == ''
+    assert resp.form['duration'].value == ''
     resp.form['start_datetime$date'] = '2016-02-16'
     resp.form['start_datetime$time'] = '17:00'
     resp.form['publication_date'] = '2020-05-11'
+    resp.form['duration'].value = 45
     resp.form['places'] = 20
     resp = resp.form.submit()
     resp = resp.follow()
     assert '/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id) in resp.text
     assert 'Feb. 16, 2016, 5 p.m.' in resp.text
     assert '20 places' in resp.text
     event.refresh_from_db()
     assert event.publication_date == datetime.date(2020, 5, 11)
+    assert event.duration == 45
+    assert event.end_datetime == event.start_datetime + datetime.timedelta(minutes=45)
 
 
 def test_edit_missing_event(app, admin_user):
     app = login(app)
     app.get('/manage/agendas/999/', status=404)
 
 
 def test_edit_event_as_manager(app, manager_user):
-- 
2.26.2

