Projet

Général

Profil

0001-misc-black-29209.patch

Lauréline Guérin, 13 décembre 2019 14:46

Télécharger (74,8 ko)

Voir les différences:

Subject: [PATCH 1/4] misc: black (#29209)

 chrono/agendas/models.py | 206 +++++++++++++++---------
 tests/test_agendas.py    |  84 +++++++---
 tests/test_manager.py    | 334 ++++++++++++++++++++++++---------------
 3 files changed, 396 insertions(+), 228 deletions(-)
chrono/agendas/models.py
71 71
    label = models.CharField(_('Label'), max_length=150)
72 72
    slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
73 73
    kind = models.CharField(_('Kind'), max_length=20, choices=AGENDA_KINDS, default='events')
74
    minimal_booking_delay = models.PositiveIntegerField(
75
            _('Minimal booking delay (in days)'), default=1)
74
    minimal_booking_delay = models.PositiveIntegerField(_('Minimal booking delay (in days)'), default=1)
76 75
    maximal_booking_delay = models.PositiveIntegerField(
77
            _('Maximal booking delay (in days)'), default=56) # eight weeks
78
    edit_role = models.ForeignKey(Group, blank=True, null=True, default=None,
79
            related_name='+', verbose_name=_('Edit Role'),
80
            on_delete=models.SET_NULL)
81
    view_role = models.ForeignKey(Group, blank=True, null=True, default=None,
82
            related_name='+', verbose_name=_('View Role'),
83
            on_delete=models.SET_NULL)
76
        _('Maximal booking delay (in days)'), default=56
77
    )  # eight weeks
78
    edit_role = models.ForeignKey(
79
        Group,
80
        blank=True,
81
        null=True,
82
        default=None,
83
        related_name='+',
84
        verbose_name=_('Edit Role'),
85
        on_delete=models.SET_NULL,
86
    )
87
    view_role = models.ForeignKey(
88
        Group,
89
        blank=True,
90
        null=True,
91
        default=None,
92
        related_name='+',
93
        verbose_name=_('View Role'),
94
        on_delete=models.SET_NULL,
95
    )
84 96

  
85 97
    class Meta:
86 98
        ordering = ['label']
......
124 136
            'permissions': {
125 137
                'view': self.view_role.name if self.view_role else None,
126 138
                'edit': self.edit_role.name if self.edit_role else None,
127
            }
139
            },
128 140
        }
129 141
        if self.kind == 'events':
130 142
            agenda['events'] = [x.export_json() for x in self.event_set.all()]
......
170 182
                Desk.import_json(desk).save()
171 183
        return created
172 184

  
185

  
173 186
WEEKDAYS_LIST = sorted(WEEKDAYS.items(), key=lambda x: x[0])
174 187

  
175 188

  
......
198 211

  
199 212
    def __str__(self):
200 213
        return u'%s / %s → %s' % (
201
                force_text(WEEKDAYS[self.weekday]),
202
                date_format(self.start_time, 'TIME_FORMAT'),
203
                date_format(self.end_time, 'TIME_FORMAT'))
214
            force_text(WEEKDAYS[self.weekday]),
215
            date_format(self.start_time, 'TIME_FORMAT'),
216
            date_format(self.end_time, 'TIME_FORMAT'),
217
        )
204 218

  
205 219
    @property
206 220
    def weekday_str(self):
......
212 226

  
213 227
    def export_json(self):
214 228
        return {
215
           'weekday': self.weekday,
216
           'start_time': self.start_time.strftime('%H:%M'),
217
           'end_time': self.end_time.strftime('%H:%M'),
229
            'weekday': self.weekday,
230
            'start_time': self.start_time.strftime('%H:%M'),
231
            'end_time': self.end_time.strftime('%H:%M'),
218 232
        }
219 233

  
220 234
    def get_time_slots(self, min_datetime, max_datetime, meeting_type):
......
223 237
        min_datetime = make_naive(min_datetime)
224 238
        max_datetime = make_naive(max_datetime)
225 239

  
226
        real_min_datetime = min_datetime + datetime.timedelta(
227
                days=self.weekday - min_datetime.weekday())
240
        real_min_datetime = min_datetime + datetime.timedelta(days=self.weekday - min_datetime.weekday())
228 241
        if real_min_datetime < min_datetime:
229 242
            real_min_datetime += datetime.timedelta(days=7)
230 243

  
231
        event_datetime = real_min_datetime.replace(hour=self.start_time.hour,
232
                minute=self.start_time.minute, second=0, microsecond=0)
244
        event_datetime = real_min_datetime.replace(
245
            hour=self.start_time.hour, minute=self.start_time.minute, second=0, microsecond=0
246
        )
233 247
        while event_datetime < max_datetime:
234 248
            end_time = event_datetime + meeting_duration
235 249
            next_time = event_datetime + duration
236 250
            if end_time.time() > self.end_time or event_datetime.date() != next_time.date():
237 251
                # back to morning
238
                event_datetime = event_datetime.replace(hour=self.start_time.hour, minute=self.start_time.minute)
252
                event_datetime = event_datetime.replace(
253
                    hour=self.start_time.hour, minute=self.start_time.minute
254
                )
239 255
                # but next week
240 256
                event_datetime += datetime.timedelta(days=7)
241 257
                next_time = event_datetime + duration
......
243 259
            if event_datetime > max_datetime:
244 260
                break
245 261

  
246
            yield TimeSlot(start_datetime=make_aware(event_datetime), meeting_type=meeting_type, desk=self.desk)
262
            yield TimeSlot(
263
                start_datetime=make_aware(event_datetime), meeting_type=meeting_type, desk=self.desk
264
            )
247 265
            event_datetime = next_time
248 266

  
249 267

  
......
265 283
    @classmethod
266 284
    def import_json(cls, data):
267 285
        meeting_type, created = cls.objects.get_or_create(
268
            slug=data['slug'], agenda=data['agenda'], defaults=data)
286
            slug=data['slug'], agenda=data['agenda'], defaults=data
287
        )
269 288
        if not created:
270 289
            for k, v in data.items():
271 290
                setattr(meeting_type, k, v)
......
273 292

  
274 293
    def export_json(self):
275 294
        return {
276
           'label': self.label,
277
           'slug': self.slug,
278
           'duration': self.duration,
295
            'label': self.label,
296
            'slug': self.slug,
297
            'duration': self.duration,
279 298
        }
280 299

  
281 300

  
......
284 303
    agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE)
285 304
    start_datetime = models.DateTimeField(_('Date/time'))
286 305
    places = models.PositiveIntegerField(_('Places'))
287
    waiting_list_places = models.PositiveIntegerField(
288
            _('Places in waiting list'), default=0)
289
    label = models.CharField(_('Label'), max_length=150, null=True, blank=True,
290
            help_text=_('Optional label to identify this date.'))
306
    waiting_list_places = models.PositiveIntegerField(_('Places in waiting list'), default=0)
307
    label = models.CharField(
308
        _('Label'),
309
        max_length=150,
310
        null=True,
311
        blank=True,
312
        help_text=_('Optional label to identify this date.'),
313
    )
291 314
    slug = models.SlugField(_('Identifier'), max_length=160, null=True, blank=True, default=None)
292
    description = models.TextField(_('Description'), null=True, blank=True,
293
            help_text=_('Optional event description.'))
315
    description = models.TextField(
316
        _('Description'), null=True, blank=True, help_text=_('Optional event description.')
317
    )
294 318
    full = models.BooleanField(default=False)
295 319
    meeting_type = models.ForeignKey(MeetingType, null=True, on_delete=models.CASCADE)
296 320
    desk = models.ForeignKey('Desk', null=True, on_delete=models.CASCADE)
......
310 334

  
311 335
    def check_full(self):
312 336
        self.full = bool(
313
            (self.booked_places >= self.places and self.waiting_list_places == 0) or
314
            (self.waiting_list_places and self.waiting_list >= self.waiting_list_places))
337
            (self.booked_places >= self.places and self.waiting_list_places == 0)
338
            or (self.waiting_list_places and self.waiting_list >= self.waiting_list_places)
339
        )
315 340

  
316 341
    def in_bookable_period(self):
317
        if localtime(now()).date() > localtime(self.start_datetime - datetime.timedelta(days=self.agenda.minimal_booking_delay)).date():
342
        if (
343
            localtime(now()).date()
344
            > localtime(
345
                self.start_datetime - datetime.timedelta(days=self.agenda.minimal_booking_delay)
346
            ).date()
347
        ):
318 348
            return False
319 349
        if self.agenda.maximal_booking_delay and (
320
                localtime(now()).date() <= localtime(self.start_datetime - datetime.timedelta(days=self.agenda.maximal_booking_delay)).date()):
350
            localtime(now()).date()
351
            <= localtime(
352
                self.start_datetime - datetime.timedelta(days=self.agenda.maximal_booking_delay)
353
            ).date()
354
        ):
321 355
            return False
322 356
        if self.start_datetime < now():
323 357
            # past the event date, we may want in the future to allow for some
......
327 361

  
328 362
    @property
329 363
    def booked_places(self):
330
        return self.booking_set.filter(cancellation_datetime__isnull=True,
331
                in_waiting_list=False).count()
364
        return self.booking_set.filter(cancellation_datetime__isnull=True, in_waiting_list=False).count()
332 365

  
333 366
    @property
334 367
    def waiting_list(self):
335
        return self.booking_set.filter(cancellation_datetime__isnull=True,
336
                in_waiting_list=True).count()
368
        return self.booking_set.filter(cancellation_datetime__isnull=True, in_waiting_list=True).count()
337 369

  
338 370
    @property
339 371
    def end_datetime(self):
......
344 376

  
345 377
    @classmethod
346 378
    def import_json(cls, data):
347
        data['start_datetime'] = make_aware(datetime.datetime.strptime(
348
            data['start_datetime'], '%Y-%m-%d %H:%M:%S'))
379
        data['start_datetime'] = make_aware(
380
            datetime.datetime.strptime(data['start_datetime'], '%Y-%m-%d %H:%M:%S')
381
        )
349 382
        if data.get('slug'):
350 383
            event, created = cls.objects.get_or_create(slug=data['slug'], defaults=data)
351 384
            if not created:
......
372 405
    in_waiting_list = models.BooleanField(default=False)
373 406
    creation_datetime = models.DateTimeField(auto_now_add=True)
374 407
    # primary booking is used to group multiple bookings together
375
    primary_booking = models.ForeignKey('self', null=True,
376
            on_delete=models.CASCADE, related_name='secondary_booking_set')
408
    primary_booking = models.ForeignKey(
409
        'self', null=True, on_delete=models.CASCADE, related_name='secondary_booking_set'
410
    )
377 411

  
378 412
    label = models.CharField(max_length=250, blank=True)
379
    user_display_label = models.CharField(verbose_name=_('Label displayed to user'), max_length=250, blank=True)
413
    user_display_label = models.CharField(
414
        verbose_name=_('Label displayed to user'), max_length=250, blank=True
415
    )
380 416
    user_name = models.CharField(max_length=250, blank=True)
381 417
    backoffice_url = models.URLField(blank=True)
382 418

  
......
405 441
        ics = vobject.iCalendar()
406 442
        ics.add('prodid').value = '-//Entr\'ouvert//NON SGML Publik'
407 443
        vevent = vobject.newFromBehavior('vevent')
408
        vevent.add('uid').value = '%s-%s-%s' % (self.event.start_datetime.isoformat(), self.event.agenda.pk, self.pk)
444
        vevent.add('uid').value = '%s-%s-%s' % (
445
            self.event.start_datetime.isoformat(),
446
            self.event.agenda.pk,
447
            self.pk,
448
        )
409 449

  
410 450
        vevent.add('summary').value = self.user_display_label or self.label
411 451
        vevent.add('dtstart').value = self.event.start_datetime
412 452
        if self.user_name:
413 453
            vevent.add('attendee').value = self.user_name
414 454
        if self.event.meeting_type:
415
            vevent.add('dtend').value = self.event.start_datetime + datetime.timedelta(minutes=self.event.meeting_type.duration)
455
            vevent.add('dtend').value = self.event.start_datetime + datetime.timedelta(
456
                minutes=self.event.meeting_type.duration
457
            )
416 458

  
417 459
        for field in ('description', 'location', 'comment', 'url'):
418 460
            field_value = request and request.GET.get(field) or self.extra_data.get(field)
......
422 464
        return ics.serialize()
423 465

  
424 466

  
425

  
426 467
@python_2_unicode_compatible
427 468
class Desk(models.Model):
428 469
    agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE)
429 470
    label = models.CharField(_('Label'), max_length=150)
430 471
    slug = models.SlugField(_('Identifier'), max_length=160)
431 472
    timeperiod_exceptions_remote_url = models.URLField(
432
            _('URL to fetch time period exceptions from'),
433
            blank=True, max_length=500)
473
        _('URL to fetch time period exceptions from'), blank=True, max_length=500
474
    )
434 475

  
435 476
    def __str__(self):
436 477
        return self.label
......
448 489
    def import_json(cls, data):
449 490
        timeperiods = data.pop('timeperiods')
450 491
        exceptions = data.pop('exceptions')
451
        instance, created = cls.objects.get_or_create(
452
            slug=data['slug'], agenda=data['agenda'], defaults=data)
492
        instance, created = cls.objects.get_or_create(slug=data['slug'], agenda=data['agenda'], defaults=data)
453 493
        if not created:
454 494
            for k, v in data.items():
455 495
                setattr(instance, k, v)
......
462 502
        return instance
463 503

  
464 504
    def export_json(self):
465
        return {'label': self.label,
466
                'slug': self.slug,
467
                'timeperiods': [time_period.export_json() for time_period in self.timeperiod_set.all()],
468
                'exceptions': [exception.export_json() for exception in self.timeperiodexception_set.all()]
469
                }
505
        return {
506
            'label': self.label,
507
            'slug': self.slug,
508
            'timeperiods': [time_period.export_json() for time_period in self.timeperiod_set.all()],
509
            'exceptions': [exception.export_json() for exception in self.timeperiodexception_set.all()],
510
        }
470 511

  
471 512
    def get_exceptions_within_two_weeks(self):
472 513
        in_two_weeks = make_aware(datetime.datetime.today() + datetime.timedelta(days=14))
473 514
        exceptions = self.timeperiodexception_set.filter(end_datetime__gte=now()).filter(
474
                Q(end_datetime__lte=in_two_weeks) | Q(start_datetime__lt=now()))
515
            Q(end_datetime__lte=in_two_weeks) | Q(start_datetime__lt=now())
516
        )
475 517
        if exceptions.exists():
476 518
            return exceptions
477 519
        # if none found within the 2 coming weeks, return the next one
478
        next_exception = self.timeperiodexception_set.filter(
479
            start_datetime__gte=now()).order_by('start_datetime').first()
520
        next_exception = (
521
            self.timeperiodexception_set.filter(start_datetime__gte=now()).order_by('start_datetime').first()
522
        )
480 523
        if next_exception:
481 524
            return [next_exception]
482 525
        return []
......
491 534
            response.raise_for_status()
492 535
        except requests.HTTPError as e:
493 536
            raise ICSError(
494
                _('Failed to retrieve remote calendar (%(url)s, HTTP error %(status_code)s).') %
495
                {'url': url, 'status_code': e.response.status_code})
537
                _('Failed to retrieve remote calendar (%(url)s, HTTP error %(status_code)s).')
538
                % {'url': url, 'status_code': e.response.status_code}
539
            )
496 540
        except requests.RequestException as e:
497 541
            raise ICSError(
498
                _('Failed to retrieve remote calendar (%(url)s, %(exception)s).') %
499
                {'url': url, 'exception': e})
542
                _('Failed to retrieve remote calendar (%(url)s, %(exception)s).')
543
                % {'url': url, 'exception': e}
544
            )
500 545

  
501 546
        return self.create_timeperiod_exceptions_from_ics(response.text, keep_synced_by_uid=True)
502 547

  
......
582 627
                        if end_dt < update_datetime:
583 628
                            TimePeriodException.objects.filter(**kwargs).update(**event)
584 629
                        else:
585
                            obj, created = TimePeriodException.objects.update_or_create(defaults=event, **kwargs)
630
                            obj, created = TimePeriodException.objects.update_or_create(
631
                                defaults=event, **kwargs
632
                            )
586 633
                            if created:
587 634
                                total_created += 1
588 635
                    # delete unseen occurrences
......
591 638

  
592 639
            if keep_synced_by_uid:
593 640
                # delete all outdated exceptions from remote calendar
594
                TimePeriodException.objects.filter(update_datetime__lt=update_datetime,
595
                                    desk=self).exclude(external_id='').delete()
641
                TimePeriodException.objects.filter(update_datetime__lt=update_datetime, desk=self).exclude(
642
                    external_id=''
643
                ).delete()
596 644

  
597 645
        return total_created
598 646

  
......
606 654
        aware_date = make_aware(datetime.datetime(date.year, date.month, date.day))
607 655
        aware_next_date = aware_date + datetime.timedelta(days=1)
608 656
        for exception in self.timeperiodexception_set.filter(
609
                start_datetime__lt=aware_next_date,
610
                end_datetime__gt=aware_date):
657
            start_datetime__lt=aware_next_date, end_datetime__gt=aware_date
658
        ):
611 659
            openslots.remove(exception.start_datetime, exception.end_datetime)
612 660

  
613 661
        return openslots.search(aware_date, aware_next_date)
......
634 682
                exc_repr = u'%s' % date_format(localtime(self.start_datetime), 'SHORT_DATE_FORMAT')
635 683
            else:
636 684
                exc_repr = u'%s → %s' % (
637
                        date_format(localtime(self.start_datetime), 'SHORT_DATE_FORMAT'),
638
                        date_format(localtime(self.end_datetime), 'SHORT_DATE_FORMAT'))
685
                    date_format(localtime(self.start_datetime), 'SHORT_DATE_FORMAT'),
686
                    date_format(localtime(self.end_datetime), 'SHORT_DATE_FORMAT'),
687
                )
639 688
        else:
640 689
            if localtime(self.start_datetime).date() == localtime(self.end_datetime).date():
641 690
                # same day
642 691
                exc_repr = u'%s → %s' % (
643
                        date_format(localtime(self.start_datetime), 'SHORT_DATETIME_FORMAT'),
644
                        date_format(localtime(self.end_datetime), 'TIME_FORMAT'))
692
                    date_format(localtime(self.start_datetime), 'SHORT_DATETIME_FORMAT'),
693
                    date_format(localtime(self.end_datetime), 'TIME_FORMAT'),
694
                )
645 695
            else:
646 696
                exc_repr = u'%s → %s' % (
647
                        date_format(localtime(self.start_datetime), 'SHORT_DATETIME_FORMAT'),
648
                        date_format(localtime(self.end_datetime), 'SHORT_DATETIME_FORMAT'))
697
                    date_format(localtime(self.start_datetime), 'SHORT_DATETIME_FORMAT'),
698
                    date_format(localtime(self.end_datetime), 'SHORT_DATETIME_FORMAT'),
699
                )
649 700

  
650 701
        if self.label:
651 702
            exc_repr = u'%s (%s)' % (self.label, exc_repr)
......
662 713
            # incomplete time period, can't tell
663 714
            return False
664 715
        for event in Event.objects.filter(
665
                desk=self.desk,
666
                booking__isnull=False,
667
                booking__cancellation_datetime__isnull=True):
716
            desk=self.desk, booking__isnull=False, booking__cancellation_datetime__isnull=True
717
        ):
668 718
            if self.start_datetime <= event.start_datetime < self.end_datetime:
669 719
                return True
670 720
        return False
tests/test_agendas.py
10 10
from django.core.management import call_command
11 11
from django.core.management.base import CommandError
12 12

  
13
from chrono.agendas.models import (Agenda, Event, Booking, MeetingType,
14
                        Desk, TimePeriod, TimePeriodException, ICSError)
13
from chrono.agendas.models import (
14
    Agenda,
15
    Event,
16
    Booking,
17
    MeetingType,
18
    Desk,
19
    TimePeriod,
20
    TimePeriodException,
21
    ICSError,
22
)
15 23

  
16 24
pytestmark = pytest.mark.django_db
17 25

  
......
154 162
    booking.save()
155 163
    assert Event.objects.all()[0].booked_places == 0
156 164

  
165

  
157 166
def test_event_bookable_period():
158 167
    agenda = Agenda(label=u'Foo bar')
159 168
    agenda.save()
......
179 188
    event.save()
180 189
    assert event.in_bookable_period() is False
181 190

  
191

  
182 192
def test_meeting_type_slugs():
183 193
    agenda1 = Agenda(label=u'Foo bar')
184 194
    agenda1.save()
......
197 207
    meeting_type3.save()
198 208
    assert meeting_type3.slug == 'baz'
199 209

  
210

  
200 211
def test_timeperiodexception_creation_from_ics():
201 212
    agenda = Agenda(label=u'Test 1 agenda')
202 213
    agenda.save()
......
206 217
    assert exceptions_count == 2
207 218
    assert TimePeriodException.objects.filter(desk=desk).count() == 2
208 219

  
220

  
209 221
def test_timeperiodexception_creation_from_ics_without_startdt():
210 222
    agenda = Agenda(label=u'Test 2 agenda')
211 223
    agenda.save()
......
222 234
        exceptions_count = desk.create_timeperiod_exceptions_from_ics(ics_sample)
223 235
    assert 'Event "Event 1" has no start date.' == str(e.value)
224 236

  
237

  
225 238
def test_timeperiodexception_creation_from_ics_without_enddt():
226 239
    agenda = Agenda(label=u'Test 3 agenda')
227 240
    agenda.save()
......
248 261
    desk.save()
249 262
    assert desk.create_timeperiod_exceptions_from_ics(ICS_SAMPLE_WITH_RECURRENT_EVENT) == 3
250 263
    assert TimePeriodException.objects.filter(desk=desk).count() == 3
251
    assert set(TimePeriodException.objects.values_list('start_datetime', flat=True)) == set([
252
        make_aware(datetime.datetime(2018, 1, 1)), make_aware(datetime.datetime(2019, 1, 1))])
264
    assert set(TimePeriodException.objects.values_list('start_datetime', flat=True)) == set(
265
        [make_aware(datetime.datetime(2018, 1, 1)), make_aware(datetime.datetime(2019, 1, 1))]
266
    )
253 267
    assert desk.create_timeperiod_exceptions_from_ics(ICS_SAMPLE_WITH_RECURRENT_EVENT) == 0
254 268
    # verify occurences are cleaned when count changed
255 269
    assert desk.create_timeperiod_exceptions_from_ics(ICS_SAMPLE_WITH_RECURRENT_EVENT_2) == 0
256 270
    assert TimePeriodException.objects.filter(desk=desk).count() == 2
257
    assert set(TimePeriodException.objects.values_list('start_datetime', flat=True)) == set([
258
        make_aware(datetime.datetime(2018, 1, 1)), make_aware(datetime.datetime(2018, 1, 2))])
271
    assert set(TimePeriodException.objects.values_list('start_datetime', flat=True)) == set(
272
        [make_aware(datetime.datetime(2018, 1, 1)), make_aware(datetime.datetime(2018, 1, 2))]
273
    )
274

  
259 275

  
260 276
def test_timeexception_creation_from_ics_with_dates():
261 277
    agenda = Agenda(label=u'Test 5 agenda')
......
275 291
        assert localtime(exception.start_datetime) == make_aware(datetime.datetime(2018, 1, 1, 0, 0))
276 292
        assert localtime(exception.end_datetime) == make_aware(datetime.datetime(2018, 1, 1, 0, 0))
277 293

  
294

  
278 295
def test_timeexception_create_from_invalid_ics():
279 296
    agenda = Agenda(label=u'Test 6 agenda')
280 297
    agenda.save()
......
284 301
        exceptions_count = desk.create_timeperiod_exceptions_from_ics(INVALID_ICS_SAMPLE)
285 302
    assert str(e.value) == 'File format is invalid.'
286 303

  
304

  
287 305
def test_timeexception_create_from_ics_with_no_events():
288 306
    agenda = Agenda(label=u'Test 7 agenda')
289 307
    agenda.save()
......
293 311
        exceptions_count = desk.create_timeperiod_exceptions_from_ics(ICS_SAMPLE_WITH_NO_EVENTS)
294 312
    assert str(e.value) == "The file doesn't contain any events."
295 313

  
314

  
296 315
@mock.patch('chrono.agendas.models.requests.get')
297 316
def test_timeperiodexception_creation_from_remote_ics(mocked_get):
298 317
    agenda = Agenda(label=u'Test 8 agenda')
......
316 335
    assert exceptions_count == 0
317 336
    TimePeriodException.objects.filter(external_id='desk-%s:' % desk.id).count() == 0
318 337

  
338

  
319 339
@mock.patch('chrono.agendas.models.requests.get')
320 340
def test_timeperiodexception_creation_from_unreachable_remote_ics(mocked_get):
321 341
    agenda = Agenda(label=u'Test 9 agenda')
......
325 345
    mocked_response = mock.Mock()
326 346
    mocked_response.text = ICS_SAMPLE
327 347
    mocked_get.return_value = mocked_response
348

  
328 349
    def mocked_requests_connection_error(*args, **kwargs):
329 350
        raise requests.ConnectionError('unreachable')
351

  
330 352
    mocked_get.side_effect = mocked_requests_connection_error
331 353
    with pytest.raises(ICSError) as e:
332 354
        exceptions_count = desk.create_timeperiod_exceptions_from_remote_ics('http://example.com/sample.ics')
333 355
    assert str(e.value) == "Failed to retrieve remote calendar (http://example.com/sample.ics, unreachable)."
334 356

  
357

  
335 358
@mock.patch('chrono.agendas.models.requests.get')
336 359
def test_timeperiodexception_creation_from_forbidden_remote_ics(mocked_get):
337 360
    agenda = Agenda(label=u'Test 10 agenda')
......
341 364
    mocked_response = mock.Mock()
342 365
    mocked_response.status_code = 403
343 366
    mocked_get.return_value = mocked_response
367

  
344 368
    def mocked_requests_http_forbidden_error(*args, **kwargs):
345 369
        raise requests.HTTPError(response=mocked_response)
370

  
346 371
    mocked_get.side_effect = mocked_requests_http_forbidden_error
347 372

  
348 373
    with pytest.raises(ICSError) as e:
349 374
        exceptions_count = desk.create_timeperiod_exceptions_from_remote_ics('http://example.com/sample.ics')
350
    assert str(e.value) == "Failed to retrieve remote calendar (http://example.com/sample.ics, HTTP error 403)."
375
    assert (
376
        str(e.value) == "Failed to retrieve remote calendar (http://example.com/sample.ics, HTTP error 403)."
377
    )
378

  
351 379

  
352 380
@mock.patch('chrono.agendas.models.requests.get')
353 381
def test_sync_desks_timeperiod_exceptions_from_ics(mocked_get, capsys):
354 382
    agenda = Agenda(label=u'Test 11 agenda')
355 383
    agenda.save()
356
    desk = Desk(label='Test 11 desk', agenda=agenda, timeperiod_exceptions_remote_url='http://example.com/sample.ics')
384
    desk = Desk(
385
        label='Test 11 desk', agenda=agenda, timeperiod_exceptions_remote_url='http://example.com/sample.ics'
386
    )
357 387
    desk.save()
358 388
    mocked_response = mock.Mock()
359 389
    mocked_response.status_code = 403
360 390
    mocked_get.return_value = mocked_response
391

  
361 392
    def mocked_requests_http_forbidden_error(*args, **kwargs):
362 393
        raise requests.HTTPError(response=mocked_response)
394

  
363 395
    mocked_get.side_effect = mocked_requests_http_forbidden_error
364 396
    call_command('sync_desks_timeperiod_exceptions')
365 397
    out, err = capsys.readouterr()
366
    assert err == 'unable to create timeperiod exceptions for "Test 11 desk": Failed to retrieve remote calendar (http://example.com/sample.ics, HTTP error 403).\n'
398
    assert (
399
        err
400
        == 'unable to create timeperiod exceptions for "Test 11 desk": Failed to retrieve remote calendar (http://example.com/sample.ics, HTTP error 403).\n'
401
    )
402

  
367 403

  
368 404
@mock.patch('chrono.agendas.models.requests.get')
369 405
def test_sync_desks_timeperiod_exceptions_from_changing_ics(mocked_get, caplog):
370 406
    agenda = Agenda(label=u'Test 11 agenda')
371 407
    agenda.save()
372
    desk = Desk(label='Test 11 desk', agenda=agenda, timeperiod_exceptions_remote_url='http:example.com/sample.ics')
408
    desk = Desk(
409
        label='Test 11 desk', agenda=agenda, timeperiod_exceptions_remote_url='http:example.com/sample.ics'
410
    )
373 411
    desk.save()
374 412
    mocked_response = mock.Mock()
375 413
    mocked_response.text = ICS_SAMPLE
......
397 435
    call_command('sync_desks_timeperiod_exceptions')
398 436
    assert not TimePeriodException.objects.filter(desk=desk).exists()
399 437

  
438

  
400 439
def test_base_meeting_duration():
401 440
    agenda = Agenda(label='Meeting', kind='meetings')
402 441
    agenda.save()
......
427 466
    exceptions_count = desk.create_timeperiod_exceptions_from_ics(ICS_SAMPLE_WITH_DURATION)
428 467
    assert exceptions_count == 2
429 468
    assert TimePeriodException.objects.filter(desk=desk).count() == 2
430
    assert set(TimePeriodException.objects.values_list('start_datetime', flat=True)) == set([
431
        make_aware(datetime.datetime(2017, 8, 31, 19, 8, 0)),
432
        make_aware(datetime.datetime(2017, 8, 30, 20, 8, 0)),
433
    ])
434
    assert set(TimePeriodException.objects.values_list('end_datetime', flat=True)) == set([
435
        make_aware(datetime.datetime(2017, 8, 31, 22, 34, 0)),
436
        make_aware(datetime.datetime(2017, 9, 1, 0, 34, 0)),
437
    ])
469
    assert set(TimePeriodException.objects.values_list('start_datetime', flat=True)) == set(
470
        [
471
            make_aware(datetime.datetime(2017, 8, 31, 19, 8, 0)),
472
            make_aware(datetime.datetime(2017, 8, 30, 20, 8, 0)),
473
        ]
474
    )
475
    assert set(TimePeriodException.objects.values_list('end_datetime', flat=True)) == set(
476
        [
477
            make_aware(datetime.datetime(2017, 8, 31, 22, 34, 0)),
478
            make_aware(datetime.datetime(2017, 9, 1, 0, 34, 0)),
479
        ]
480
    )
438 481

  
439 482

  
440 483
@pytest.mark.freeze_time('2017-12-01')
......
447 490
    desk.save()
448 491
    assert desk.create_timeperiod_exceptions_from_ics(ICS_SAMPLE_WITH_RECURRENT_EVENT_IN_THE_PAST) == 2
449 492
    assert TimePeriodException.objects.filter(desk=desk).count() == 2
450
    assert set(TimePeriodException.objects.values_list('start_datetime', flat=True)) == set([
451
        make_aware(datetime.datetime(2018, 1, 1)), make_aware(datetime.datetime(2019, 1, 1))])
493
    assert set(TimePeriodException.objects.values_list('start_datetime', flat=True)) == set(
494
        [make_aware(datetime.datetime(2018, 1, 1)), make_aware(datetime.datetime(2019, 1, 1))]
495
    )
452 496
    assert desk.create_timeperiod_exceptions_from_ics(ICS_SAMPLE_WITH_RECURRENT_EVENT_IN_THE_PAST) == 0
453 497

  
454 498

  
tests/test_manager.py
16 16

  
17 17
from chrono.wsgi import application
18 18

  
19
from chrono.agendas.models import (Agenda, Event, Booking, MeetingType,
20
                                   TimePeriod, Desk, TimePeriodException)
19
from chrono.agendas.models import Agenda, Event, Booking, MeetingType, TimePeriod, Desk, TimePeriodException
21 20
from chrono.manager.utils import export_site
22 21

  
23 22
pytestmark = pytest.mark.django_db
24 23

  
24

  
25 25
@pytest.fixture
26 26
def simple_user():
27 27
    try:
......
30 30
        user = User.objects.create_user('user', password='user')
31 31
    return user
32 32

  
33

  
33 34
@pytest.fixture
34 35
def manager_user():
35 36
    try:
......
42 43
    user.groups.set([group])
43 44
    return user
44 45

  
46

  
45 47
@pytest.fixture
46 48
def admin_user():
47 49
    try:
......
50 52
        user = User.objects.create_superuser('admin', email=None, password='admin')
51 53
    return user
52 54

  
55

  
53 56
@pytest.fixture
54 57
def api_user():
55 58
    try:
56 59
        user = User.objects.get(username='api-user')
57 60
    except User.DoesNotExist:
58
        user = User.objects.create(username='john.doe',
59
                first_name=u'John', last_name=u'Doe', email='john.doe@example.net')
61
        user = User.objects.create(
62
            username='john.doe', first_name=u'John', last_name=u'Doe', email='john.doe@example.net'
63
        )
60 64
        user.set_password('password')
61 65
        user.save()
62 66
    return user
63 67

  
68

  
64 69
def login(app, username='admin', password='admin'):
65 70
    login_page = app.get('/login/')
66 71
    login_form = login_page.forms[0]
......
70 75
    assert resp.status_int == 302
71 76
    return app
72 77

  
78

  
73 79
def test_unlogged_access(app):
74 80
    # connect while not being logged in
75 81
    assert app.get('/manage/', status=302).location.endswith('/login/?next=/manage/')
76 82

  
83

  
77 84
def test_simple_user_access(app, simple_user):
78 85
    # connect while being logged as a simple user, access should be forbidden
79 86
    app = login(app, username='user', password='user')
80 87
    assert app.get('/manage/', status=403)
81 88

  
89

  
82 90
def test_manager_user_access(app, manager_user):
83 91
    # connect while being logged as a manager user, access should be granted if
84 92
    # there's at least an agenda that is viewable or editable.
......
99 107
    agenda.save()
100 108
    assert app.get('/manage/', status=200)
101 109

  
110

  
102 111
def test_home_redirect(app):
103 112
    assert app.get('/', status=302).location.endswith('/manage/')
104 113

  
114

  
105 115
def test_access(app, admin_user):
106 116
    app = login(app)
107 117
    resp = app.get('/manage/', status=200)
108 118
    assert '<h2>Agendas</h2>' in resp.text
109 119
    assert "This site doesn't have any agenda yet." in resp.text
110 120

  
121

  
111 122
def test_logout(app, admin_user):
112 123
    app = login(app)
113 124
    app.get('/logout/')
114 125
    assert app.get('/manage/', status=302).location.endswith('/login/?next=/manage/')
115 126

  
127

  
116 128
def test_menu_json(app, admin_user):
117 129
    app = login(app)
118 130
    resp = app.get('/manage/menu.json', status=200)
......
123 135
    assert resp2.text == 'Q(%s);' % resp.text
124 136
    assert resp2.content_type == 'application/javascript'
125 137

  
138

  
126 139
def test_view_agendas_as_manager(app, manager_user):
127 140
    agenda = Agenda(label=u'Foo Bar')
128 141
    agenda.view_role = manager_user.groups.all()[0]
......
156 169
    # check it gives a 404 on unknown agendas
157 170
    resp = app.get('/manage/agendas/%s/settings' % '9999', status=404)
158 171

  
172

  
159 173
def test_add_agenda(app, admin_user):
160 174
    app = login(app)
161 175
    resp = app.get('/manage/', status=200)
......
169 183
    assert 'Foo bar' in resp.text
170 184
    assert '<h2>Settings' in resp.text
171 185

  
186

  
172 187
def test_add_agenda_as_manager(app, manager_user):
173 188
    # open /manage/ access to manager_user, and check agenda creation is not
174 189
    # allowed.
......
179 194
    resp = app.get('/manage/', status=200)
180 195
    resp = app.get('/manage/agendas/add/', status=403)
181 196

  
197

  
182 198
def test_options_agenda(app, admin_user):
183 199
    agenda = Agenda(label=u'Foo bar')
184 200
    agenda.save()
......
194 210
    assert 'Foo baz' in resp.text
195 211
    assert '<h2>Settings' in resp.text
196 212

  
213

  
197 214
def test_options_agenda_as_manager(app, manager_user):
198 215
    agenda = Agenda(label=u'Foo bar')
199 216
    agenda.view_role = manager_user.groups.all()[0]
......
227 244
    assert 'Foo baz' in resp.text
228 245
    assert '<h2>Settings' in resp.text
229 246

  
247

  
230 248
def test_delete_agenda(app, admin_user):
231 249
    agenda = Agenda(label=u'Foo bar')
232 250
    agenda.save()
......
239 257
    resp = resp.follow()
240 258
    assert not 'Foo bar' in resp.text
241 259

  
260

  
242 261
def test_delete_busy_agenda(app, admin_user):
243 262
    agenda = Agenda(label=u'Foo bar')
244 263
    agenda.save()
245
    event = Event(start_datetime=now() + datetime.timedelta(days=10),
246
                  places=10, agenda=agenda)
264
    event = Event(start_datetime=now() + datetime.timedelta(days=10), places=10, agenda=agenda)
247 265
    event.save()
248 266

  
249 267
    app = login(app)
......
272 290
    booking.save()
273 291
    resp = resp.form.submit(status=403)
274 292

  
293

  
275 294
def test_delete_agenda_as_manager(app, manager_user):
276 295
    agenda = Agenda(label=u'Foo bar')
277 296
    agenda.edit_role = manager_user.groups.all()[0]
......
290 309
    desk_a = Desk.objects.create(agenda=agenda, label='Desk A')
291 310
    desk_b = Desk.objects.create(agenda=agenda, label='Desk B')
292 311

  
293
    event = Event(start_datetime=now() + datetime.timedelta(days=10),
294
                  places=10, agenda=agenda, desk=desk_a)
312
    event = Event(start_datetime=now() + datetime.timedelta(days=10), places=10, agenda=agenda, desk=desk_a)
295 313
    event.save()
296 314

  
297 315
    app = login(app)
......
354 372
    app = login(app)
355 373
    app.get('/manage/agendas/%s/add-event' % '999', status=404)
356 374

  
375

  
357 376
def test_add_event_as_manager(app, manager_user):
358 377
    agenda = Agenda(label=u'Foo bar')
359 378
    agenda.view_role = manager_user.groups.all()[0]
......
378 397
    assert 'Feb. 15, 2016, 5 p.m.' in resp.text
379 398
    assert '10 places' in resp.text
380 399

  
400

  
381 401
def test_edit_event(app, admin_user):
382 402
    agenda = Agenda(label=u'Foo bar')
383 403
    agenda.save()
384
    event = Event(
385
            start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
386
            places=20, agenda=agenda)
404
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=20, agenda=agenda)
387 405
    event.save()
388 406
    app = login(app)
389 407
    resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
......
397 415
    assert 'Feb. 16, 2016, 5 p.m.' in resp.text
398 416
    assert '20 places' in resp.text
399 417

  
418

  
400 419
def test_edit_missing_event(app, admin_user):
401 420
    app = login(app)
402 421
    app.get('/manage/agendas/999/', status=404)
403 422

  
423

  
404 424
def test_edit_event_as_manager(app, manager_user):
405 425
    agenda = Agenda(label=u'Foo bar')
406 426
    agenda.view_role = manager_user.groups.all()[0]
407 427
    agenda.save()
408
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
409
                  places=20, agenda=agenda)
428
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=20, agenda=agenda)
410 429
    event.save()
411 430
    app = login(app, username='manager', password='manager')
412 431
    resp = app.get('/manage/events/%s/' % event.id, status=403)
......
424 443
    assert 'Feb. 16, 2016, 5 p.m.' in resp.text
425 444
    assert '20 places' in resp.text
426 445

  
446

  
427 447
def test_booked_places(app, admin_user):
428 448
    agenda = Agenda(label=u'Foo bar')
429 449
    agenda.save()
430
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
431
                  places=10, agenda=agenda)
450
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
432 451
    event.save()
433 452
    Booking(event=event).save()
434 453
    Booking(event=event).save()
......
437 456
    assert '10 places' in resp.text
438 457
    assert '2 booked places' in resp.text
439 458

  
459

  
440 460
def test_event_classes(app, admin_user):
441 461
    agenda = Agenda(label=u'Foo bar')
442 462
    agenda.save()
443
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
444
                  places=10, agenda=agenda)
463
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
445 464
    event.save()
446 465
    for i in range(2):
447 466
        Booking(event=event).save()
......
463 482
    assert 'full' in resp.text
464 483
    assert 'overbooking' in resp.text
465 484

  
485

  
466 486
def test_delete_event(app, admin_user):
467 487
    agenda = Agenda(label=u'Foo bar')
468 488
    agenda.save()
469
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
470
                  places=10, agenda=agenda)
489
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
471 490
    event.save()
472 491

  
473 492
    app = login(app)
......
478 497
    assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
479 498
    assert Event.objects.count() == 0
480 499

  
500

  
481 501
def test_delete_busy_event(app, admin_user):
482 502
    agenda = Agenda(label=u'Foo bar')
483 503
    agenda.save()
484
    event = Event(start_datetime=now() + datetime.timedelta(days=10),
485
                  places=10, agenda=agenda)
504
    event = Event(start_datetime=now() + datetime.timedelta(days=10), places=10, agenda=agenda)
486 505
    event.save()
487 506

  
488 507
    app = login(app)
......
511 530
    booking.save()
512 531
    resp = resp.form.submit(status=403)
513 532

  
533

  
514 534
def test_delete_event_as_manager(app, manager_user):
515 535
    agenda = Agenda(label=u'Foo bar')
516 536
    agenda.edit_role = manager_user.groups.all()[0]
517 537
    agenda.save()
518
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
519
                  places=10, agenda=agenda)
538
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
520 539
    event.save()
521 540

  
522 541
    app = login(app, username='manager', password='manager')
......
527 546
    assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
528 547
    assert Event.objects.count() == 0
529 548

  
549

  
530 550
def test_import_events(app, admin_user):
531 551
    agenda = Agenda(label=u'Foo bar')
532 552
    agenda.save()
......
590 610
    Event.objects.all().delete()
591 611

  
592 612
    resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
593
    resp.form['events_csv_file'] = Upload('t.csv',
594
            u'2016-09-16,18:00,10,5,éléphant'.encode('utf-8'), 'text/csv')
613
    resp.form['events_csv_file'] = Upload(
614
        't.csv', u'2016-09-16,18:00,10,5,éléphant'.encode('utf-8'), 'text/csv'
615
    )
595 616
    resp = resp.form.submit(status=302)
596 617
    assert Event.objects.count() == 1
597 618
    assert Event.objects.all()[0].start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
......
601 622
    Event.objects.all().delete()
602 623

  
603 624
    resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
604
    resp.form['events_csv_file'] = Upload('t.csv',
605
            u'2016-09-16,18:00,10,5,éléphant'.encode('iso-8859-15'), 'text/csv')
625
    resp.form['events_csv_file'] = Upload(
626
        't.csv', u'2016-09-16,18:00,10,5,éléphant'.encode('iso-8859-15'), 'text/csv'
627
    )
606 628
    resp = resp.form.submit(status=302)
607 629
    assert Event.objects.count() == 1
608 630
    assert Event.objects.all()[0].start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
......
612 634
    Event.objects.all().delete()
613 635

  
614 636
    resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
615
    resp.form['events_csv_file'] = Upload('t.csv',
616
            u'2016-09-16,18:00,10,5,éléphant'.encode('eucjp'), 'text/csv')
637
    resp.form['events_csv_file'] = Upload(
638
        't.csv', u'2016-09-16,18:00,10,5,éléphant'.encode('eucjp'), 'text/csv'
639
    )
617 640
    resp = resp.form.submit(status=302)
618 641
    assert Event.objects.count() == 1
619 642
    assert Event.objects.all()[0].start_datetime == make_aware(datetime.datetime(2016, 9, 16, 18, 0))
......
623 646
    Event.objects.all().delete()
624 647

  
625 648
    resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
626
    resp.form['events_csv_file'] = Upload('t.csv', b'date,time,etc.\n'
627
                                                   b'2016-09-16,18:00,10,5,bla bla bla\n'
628
                                                   b'\n'
629
                                                   b'2016-09-19,18:00,10', 'text/csv')
649
    resp.form['events_csv_file'] = Upload(
650
        't.csv',
651
        b'date,time,etc.\n' b'2016-09-16,18:00,10,5,bla bla bla\n' b'\n' b'2016-09-19,18:00,10',
652
        'text/csv',
653
    )
630 654
    resp = resp.form.submit(status=302)
631 655
    assert Event.objects.count() == 2
632 656
    Event.objects.all().delete()
633 657

  
634 658
    resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
635
    resp.form['events_csv_file'] = Upload('t.csv', '"date"\t"time"\t"etc."\n'
636
                                                   '"2016-09-16"\t"18:00"\t"10"\t"5"\t"éléphant"\n'
637
                                                   '"2016-09-19"\t"18:00"\t"10"'.encode('iso-8859-15'), 'text/csv')
659
    resp.form['events_csv_file'] = Upload(
660
        't.csv',
661
        '"date"\t"time"\t"etc."\n'
662
        '"2016-09-16"\t"18:00"\t"10"\t"5"\t"éléphant"\n'
663
        '"2016-09-19"\t"18:00"\t"10"'.encode('iso-8859-15'),
664
        'text/csv',
665
    )
638 666
    resp = resp.form.submit(status=302)
639 667
    assert Event.objects.count() == 2
640 668
    Event.objects.all().delete()
......
671 699
    agenda = Agenda.objects.get(label='Foo bar')
672 700
    assert agenda.kind == 'meetings'
673 701

  
702

  
674 703
def test_meetings_agenda_add_meeting_type(app, admin_user):
675 704
    agenda = Agenda(label=u'Foo bar', kind='meetings')
676 705
    agenda.save()
......
693 722
    resp = resp.form.submit()
694 723
    assert MeetingType.objects.get(agenda=agenda).duration == 30
695 724

  
725

  
696 726
def test_meetings_agenda_delete_meeting_type(app, admin_user):
697 727
    agenda = Agenda(label=u'Foo bar', kind='meetings')
698 728
    agenda.save()
......
709 739
    assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
710 740
    assert MeetingType.objects.count() == 0
711 741

  
742

  
712 743
def test_meetings_agenda_add_time_period(app, admin_user):
713 744
    agenda = Agenda(label=u'Foo bar', kind='meetings')
714 745
    agenda.save()
......
771 802
    resp = resp.form.submit()
772 803
    assert TimePeriod.objects.filter(desk=desk).count() == 4
773 804

  
805

  
774 806
def test_meetings_agenda_delete_time_period(app, admin_user):
775 807
    agenda = Agenda(label=u'Foo bar', kind='meetings')
776 808
    agenda.save()
......
778 810
    MeetingType(agenda=agenda, label='Blah').save()
779 811

  
780 812
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
781
    time_period = TimePeriod(desk=desk, weekday=2,
782
            start_time=datetime.time(10, 0),
783
            end_time=datetime.time(18, 0))
813
    time_period = TimePeriod(
814
        desk=desk, weekday=2, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
815
    )
784 816
    time_period.save()
785 817

  
786 818
    app = login(app)
......
811 843
    resp = app.get('/manage/agendas/%d/settings' % agenda.id, status=403)
812 844
    MeetingType(agenda=agenda, label='Blah').save()
813 845
    app.get('/manage/agendas/%d/desk/%d/add-time-period' % (agenda.id, desk.id), status=403)
814
    time_period = TimePeriod(desk=desk, weekday=0, start_time=datetime.time(9, 0),
815
                             end_time=datetime.time(12, 0))
846
    time_period = TimePeriod(
847
        desk=desk, weekday=0, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
848
    )
816 849
    time_period.save()
817 850
    resp = app.get('/manage/agendas/%d/' % agenda.id)
818 851
    app.get('/manage/timeperiods/%d/edit' % time_period.id, status=403)
......
915 948
    resp = resp.form.submit().follow()
916 949
    assert TimePeriodException.objects.count() == 1
917 950
    time_period_exception = TimePeriodException.objects.first()
918
    assert localtime(time_period_exception.start_datetime).strftime(dt_format) == tomorrow.replace(hour=8).strftime(dt_format)
919
    assert localtime(time_period_exception.end_datetime).strftime(dt_format) == tomorrow.replace(hour=16).strftime(dt_format)
951
    assert localtime(time_period_exception.start_datetime).strftime(dt_format) == tomorrow.replace(
952
        hour=8
953
    ).strftime(dt_format)
954
    assert localtime(time_period_exception.end_datetime).strftime(dt_format) == tomorrow.replace(
955
        hour=16
956
    ).strftime(dt_format)
920 957
    # add an exception beyond 2 weeks and make sure it isn't listed
921 958
    resp = resp.click('Add a time period exception', index=1)
922 959
    future = tomorrow + datetime.timedelta(days=15)
......
927 964
    assert TimePeriodException.objects.count() == 2
928 965
    assert 'Exception 1' in resp.text
929 966
    assert 'Exception 2' not in resp.text
930
    resp = resp.click(href="/manage/time-period-exceptions/%d/exception-extract-list" % agenda.desk_set.first().pk)
967
    resp = resp.click(
968
        href="/manage/time-period-exceptions/%d/exception-extract-list" % agenda.desk_set.first().pk
969
    )
931 970
    assert 'Exception 1' in resp.text
932 971
    assert 'Exception 2' in resp.text
933 972

  
......
936 975
    agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
937 976
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
938 977
    MeetingType(agenda=agenda, label='Blah').save()
939
    TimePeriod.objects.create(weekday=1, desk=desk,
940
                              start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
941
    event = Event.objects.create(agenda=agenda, places=1, desk=desk,
942
                                 start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30)))
978
    TimePeriod.objects.create(
979
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
980
    )
981
    event = Event.objects.create(
982
        agenda=agenda, places=1, desk=desk, start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30))
983
    )
943 984
    Booking.objects.create(event=event)
944 985
    login(app)
945 986
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
946 987
    resp = resp.click('Settings')
947 988
    resp = resp.click('Add a time period exception')
948
    resp = resp.form.submit() # submit empty form
989
    resp = resp.form.submit()  # submit empty form
949 990
    # fields should be marked with errors
950 991
    assert resp.text.count('This field is required.') == 2
951 992
    # try again with data in fields
......
957 998

  
958 999
    # check it's possible to add an exception on another desk
959 1000
    desk = Desk.objects.create(agenda=agenda, label='Desk B')
960
    TimePeriod.objects.create(weekday=1, desk=desk,
961
                              start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1001
    TimePeriod.objects.create(
1002
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1003
    )
962 1004
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
963 1005
    resp = resp.click('Settings')
964 1006
    resp = resp.click('Add a time period exception', href='desk/%s/' % desk.id)
......
967 1009
    resp = resp.form.submit()
968 1010
    assert TimePeriodException.objects.count() == 1
969 1011

  
1012

  
970 1013
def test_meetings_agenda_add_time_period_exception_when_cancelled_booking_exists(app, admin_user):
971 1014
    agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
972 1015
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
973 1016
    MeetingType(agenda=agenda, label='Blah').save()
974
    TimePeriod.objects.create(weekday=1, desk=desk,
975
                              start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
976
    event = Event.objects.create(agenda=agenda, places=1,
977
                                 start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30)))
978
    Booking.objects.create(event=event,
979
            cancellation_datetime=make_aware(datetime.datetime(2017, 5, 20, 10, 30)))
1017
    TimePeriod.objects.create(
1018
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1019
    )
1020
    event = Event.objects.create(
1021
        agenda=agenda, places=1, start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 30))
1022
    )
1023
    Booking.objects.create(
1024
        event=event, cancellation_datetime=make_aware(datetime.datetime(2017, 5, 20, 10, 30))
1025
    )
980 1026
    login(app)
981 1027
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
982 1028
    resp = resp.click('Settings')
......
987 1033
    assert 'One or several bookings exists within this time slot.' not in resp.text
988 1034
    assert TimePeriodException.objects.count() == 1
989 1035

  
1036

  
990 1037
def test_meetings_agenda_add_invalid_time_period_exception(app, admin_user):
991 1038
    agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
992 1039
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
993 1040
    MeetingType(agenda=agenda, label='Blah').save()
994
    TimePeriod.objects.create(weekday=1, desk=desk,
995
                              start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1041
    TimePeriod.objects.create(
1042
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1043
    )
996 1044
    login(app)
997 1045
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
998 1046
    resp = resp.click('Settings')
......
1007 1055
    agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
1008 1056
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
1009 1057
    MeetingType(agenda=agenda, label='Blah').save()
1010
    TimePeriod.objects.create(weekday=1, desk=desk,
1011
                              start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1058
    TimePeriod.objects.create(
1059
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1060
    )
1012 1061
    login(app)
1013 1062
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1014 1063
    resp = resp.click('Settings')
......
1022 1071
    resp = resp.form.submit().follow()
1023 1072
    assert TimePeriodException.objects.count() == 1
1024 1073
    time_period_exception = TimePeriodException.objects.first()
1025
    assert localtime(time_period_exception.start_datetime).strftime(dt_format) == tomorrow.replace(hour=8).strftime(dt_format)
1026
    assert localtime(time_period_exception.end_datetime).strftime(dt_format) == tomorrow.replace(hour=16).strftime(dt_format)
1074
    assert localtime(time_period_exception.start_datetime).strftime(dt_format) == tomorrow.replace(
1075
        hour=8
1076
    ).strftime(dt_format)
1077
    assert localtime(time_period_exception.end_datetime).strftime(dt_format) == tomorrow.replace(
1078
        hour=16
1079
    ).strftime(dt_format)
1027 1080
    resp = resp.click(href='/manage/time-period-exceptions/%d/edit' % time_period_exception.id)
1028 1081
    resp = resp.click('Delete')
1029 1082
    resp = resp.form.submit().follow()
......
1035 1088
        label='Future Exception',
1036 1089
        desk=desk,
1037 1090
        start_datetime=now() + datetime.timedelta(days=1),
1038
        end_datetime=now() + datetime.timedelta(days=2))
1091
        end_datetime=now() + datetime.timedelta(days=2),
1092
    )
1039 1093
    resp = app.get('/manage/time-period-exceptions/%d/exception-list' % desk.pk)
1040 1094
    resp = resp.click(href='/manage/time-period-exceptions/%d/delete' % time_period_exception.pk)
1041 1095
    resp = resp.form.submit(
......
1048 1102
    agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
1049 1103
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
1050 1104
    MeetingType(agenda=agenda, label='Blah').save()
1051
    TimePeriod.objects.create(weekday=1, desk=desk,
1052
                              start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1105
    TimePeriod.objects.create(
1106
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1107
    )
1053 1108
    past_exception = TimePeriodException.objects.create(
1054 1109
        label='Past Exception',
1055 1110
        desk=desk,
1056 1111
        start_datetime=now() - datetime.timedelta(days=2),
1057
        end_datetime=now() - datetime.timedelta(days=1))
1112
        end_datetime=now() - datetime.timedelta(days=1),
1113
    )
1058 1114
    current_exception = TimePeriodException.objects.create(
1059 1115
        label='Current Exception',
1060 1116
        desk=desk,
1061 1117
        start_datetime=now() - datetime.timedelta(days=1),
1062
        end_datetime=now() + datetime.timedelta(days=1))
1118
        end_datetime=now() + datetime.timedelta(days=1),
1119
    )
1063 1120
    future_exception = TimePeriodException.objects.create(
1064 1121
        label='Future Exception',
1065 1122
        desk=desk,
1066 1123
        start_datetime=now() + datetime.timedelta(days=1),
1067
        end_datetime=now() + datetime.timedelta(days=2))
1124
        end_datetime=now() + datetime.timedelta(days=2),
1125
    )
1068 1126

  
1069 1127
    login(app)
1070 1128
    resp = app.get('/manage/agendas/%d/settings' % agenda.pk)
......
1092 1150
    resp = resp.click('Settings')
1093 1151
    assert 'Import exceptions from .ics' not in resp.text
1094 1152

  
1095
    TimePeriod.objects.create(weekday=1, desk=desk,
1096
                start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1153
    TimePeriod.objects.create(
1154
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1155
    )
1097 1156

  
1098 1157
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1099 1158
    resp = resp.click('Settings')
......
1150 1209
    agenda = Agenda.objects.create(label='Example', kind='meetings')
1151 1210
    desk = Desk.objects.create(agenda=agenda, label='Test Desk')
1152 1211
    MeetingType(agenda=agenda, label='Foo').save()
1153
    TimePeriod.objects.create(weekday=1, desk=desk,
1154
                              start_time=datetime.time(10, 0),
1155
                              end_time=datetime.time(12, 0))
1212
    TimePeriod.objects.create(
1213
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1214
    )
1156 1215
    login(app)
1157 1216
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1158 1217
    resp = resp.click('Settings')
......
1182 1241
    resp = resp.click('Settings')
1183 1242
    assert 'Import exceptions from .ics' not in resp.text
1184 1243

  
1185
    TimePeriod.objects.create(weekday=1, desk=desk,
1186
                start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1244
    TimePeriod.objects.create(
1245
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1246
    )
1187 1247

  
1188 1248
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1189 1249
    resp = resp.click('Settings')
......
1193 1253
    assert 'ics_url' in resp.form.fields
1194 1254
    resp.form['ics_url'] = 'http://example.com/foo.ics'
1195 1255
    mocked_response = mock.Mock()
1196
    mocked_response.text =  """BEGIN:VCALENDAR
1256
    mocked_response.text = """BEGIN:VCALENDAR
1197 1257
VERSION:2.0
1198 1258
PRODID:-//foo.bar//EN
1199 1259
BEGIN:VEVENT
......
1213 1273
    resp = resp.click('upload')
1214 1274
    resp.form['ics_url'] = ''
1215 1275
    resp = resp.form.submit(status=302)
1216
    assert not TimePeriodException.objects.filter(desk=desk,
1217
                    external_id='desk-%s:random-event-id' % desk.id).exists()
1276
    assert not TimePeriodException.objects.filter(
1277
        desk=desk, external_id='desk-%s:random-event-id' % desk.id
1278
    ).exists()
1279

  
1218 1280

  
1219 1281
@mock.patch('chrono.agendas.models.requests.get')
1220 1282
def test_agenda_import_time_period_exception_with_remote_ics_no_events(mocked_get, app, admin_user):
......
1226 1288
    resp = resp.click('Settings')
1227 1289
    assert 'Import exceptions from .ics' not in resp.text
1228 1290

  
1229
    TimePeriod.objects.create(weekday=1, desk=desk,
1230
                start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1291
    TimePeriod.objects.create(
1292
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1293
    )
1231 1294

  
1232 1295
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1233 1296
    resp = resp.click('Settings')
1234 1297
    resp = resp.click('upload')
1235 1298
    resp.form['ics_url'] = 'http://example.com/foo.ics'
1236 1299
    mocked_response = mock.Mock()
1237
    mocked_response.text =  """BEGIN:VCALENDAR
1300
    mocked_response.text = """BEGIN:VCALENDAR
1238 1301
VERSION:2.0
1239 1302
PRODID:-//foo.bar//EN
1240 1303
BEGIN:VEVENT
......
1257 1320
    resp = resp.click('Settings')
1258 1321
    resp = resp.click('upload')
1259 1322
    resp = resp.form.submit(status=302)
1260
    assert not TimePeriodException.objects.filter(desk=desk,
1261
                    external_id='random-event-id').exists()
1323
    assert not TimePeriodException.objects.filter(desk=desk, external_id='random-event-id').exists()
1262 1324

  
1263 1325

  
1264 1326
@mock.patch('chrono.agendas.models.requests.get')
......
1271 1333
    resp = resp.click('Settings')
1272 1334
    assert 'Import exceptions from .ics' not in resp.text
1273 1335

  
1274
    TimePeriod.objects.create(weekday=1, desk=desk,
1275
                start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1336
    TimePeriod.objects.create(
1337
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1338
    )
1276 1339

  
1277 1340
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1278 1341
    resp = resp.click('Settings')
1279 1342
    resp = resp.click('upload')
1280 1343
    resp.form['ics_url'] = 'http://example.com/foo.ics'
1281 1344
    mocked_response = mock.Mock()
1282
    mocked_response.text =  """BEGIN:VCALENDAR
1345
    mocked_response.text = """BEGIN:VCALENDAR
1283 1346
VERSION:2.0
1284 1347
PRODID:-//foo.bar//EN
1285 1348
BEGIN:VEVENT
......
1316 1379
    resp = resp.form.submit(status=302)
1317 1380
    assert TimePeriodException.objects.filter(desk=desk).count() == 1
1318 1381

  
1382

  
1319 1383
@mock.patch('chrono.agendas.models.requests.get')
1320
def test_agenda_import_time_period_exception_from_remote_ics_with_connection_error(mocked_get, app, admin_user):
1384
def test_agenda_import_time_period_exception_from_remote_ics_with_connection_error(
1385
    mocked_get, app, admin_user
1386
):
1321 1387
    agenda = Agenda.objects.create(label='New Example', kind='meetings')
1322 1388
    desk = Desk.objects.create(agenda=agenda, label='New Desk')
1323 1389
    MeetingType(agenda=agenda, label='Bar').save()
......
1326 1392
    resp = resp.click('Settings')
1327 1393
    assert 'Import exceptions from .ics' not in resp.text
1328 1394

  
1329
    TimePeriod.objects.create(weekday=1, desk=desk,
1330
                start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1395
    TimePeriod.objects.create(
1396
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1397
    )
1331 1398

  
1332 1399
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1333 1400
    resp = resp.click('Settings')
......
1338 1405
    resp.form['ics_url'] = 'http://example.com/foo.ics'
1339 1406
    mocked_response = mock.Mock()
1340 1407
    mocked_get.return_value = mocked_response
1408

  
1341 1409
    def mocked_requests_connection_error(*args, **kwargs):
1342 1410
        raise requests.exceptions.ConnectionError('unreachable')
1411

  
1343 1412
    mocked_get.side_effect = mocked_requests_connection_error
1344 1413
    resp = resp.form.submit(status=200)
1345 1414
    assert 'Failed to retrieve remote calendar (http://example.com/foo.ics, unreachable).' in resp.text
1346 1415

  
1416

  
1347 1417
@mock.patch('chrono.agendas.models.requests.get')
1348 1418
def test_agenda_import_time_period_exception_from_forbidden_remote_ics(mocked_get, app, admin_user):
1349 1419
    agenda = Agenda.objects.create(label='New Example', kind='meetings')
......
1354 1424
    resp = resp.click('Settings')
1355 1425
    assert 'Import exceptions from .ics' not in resp.text
1356 1426

  
1357
    TimePeriod.objects.create(weekday=1, desk=desk,
1358
                start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1427
    TimePeriod.objects.create(
1428
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1429
    )
1359 1430

  
1360 1431
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1361 1432
    resp = resp.click('Settings')
......
1364 1435
    mocked_response = mock.Mock()
1365 1436
    mocked_response.status_code = 403
1366 1437
    mocked_get.return_value = mocked_response
1438

  
1367 1439
    def mocked_requests_http_forbidden_error(*args, **kwargs):
1368 1440
        raise requests.exceptions.HTTPError(response=mocked_response)
1441

  
1369 1442
    mocked_get.side_effect = mocked_requests_http_forbidden_error
1370 1443
    resp = resp.form.submit(status=200)
1371 1444
    assert 'Failed to retrieve remote calendar (http://example.com/foo.ics, HTTP error 403).' in resp.text
1372 1445

  
1446

  
1373 1447
@mock.patch('chrono.agendas.models.requests.get')
1374 1448
def test_agenda_import_time_period_exception_from_remote_ics_with_ssl_error(mocked_get, app, admin_user):
1375 1449
    agenda = Agenda.objects.create(label='New Example', kind='meetings')
......
1379 1453
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1380 1454
    resp = resp.click('Settings')
1381 1455
    assert 'Import exceptions from .ics' not in resp.text
1382
    TimePeriod.objects.create(weekday=1, desk=desk,
1383
                start_time=datetime.time(10, 0), end_time=datetime.time(12, 0))
1456
    TimePeriod.objects.create(
1457
        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
1458
    )
1384 1459

  
1385 1460
    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
1386 1461
    resp = resp.click('Settings')
......
1388 1463
    resp.form['ics_url'] = 'https://example.com/foo.ics'
1389 1464
    mocked_response = mock.Mock()
1390 1465
    mocked_get.return_value = mocked_response
1466

  
1391 1467
    def mocked_requests_http_ssl_error(*args, **kwargs):
1392 1468
        raise requests.exceptions.SSLError('SSL error')
1469

  
1393 1470
    mocked_get.side_effect = mocked_requests_http_ssl_error
1394 1471
    resp = resp.form.submit(status=200)
1395 1472
    assert 'Failed to retrieve remote calendar (https://example.com/foo.ics, SSL error).' in resp.text
1396 1473

  
1474

  
1397 1475
def test_agenda_day_view(app, admin_user, manager_user, api_user):
1398 1476
    agenda = Agenda.objects.create(label='New Example', kind='meetings')
1399 1477
    desk = Desk.objects.create(agenda=agenda, label='New Desk')
......
1409 1487
    resp = resp.follow()
1410 1488
    assert 'No opening hours this day.' in resp.text  # no time pediod
1411 1489

  
1412
    timeperiod = TimePeriod(desk=desk, weekday=today.weekday(),
1413
            start_time=datetime.time(10, 0),
1414
            end_time=datetime.time(18, 0))
1490
    timeperiod = TimePeriod(
1491
        desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
1492
    )
1415 1493
    timeperiod.save()
1416 1494
    resp = app.get('/manage/agendas/%s/' % agenda.id, status=302).follow()
1417 1495
    assert not 'No opening hours this day.' in resp.text
......
1434 1512
    booking_url = resp.json['data'][0]['api']['fillslot_url']
1435 1513
    booking_url2 = resp.json['data'][2]['api']['fillslot_url']
1436 1514
    resp = app.post(booking_url)
1437
    resp = app.post_json(booking_url2,
1438
            params={'label': 'foo', 'user': 'bar', 'url': 'http://baz/'})
1515
    resp = app.post_json(booking_url2, params={'label': 'foo', 'user': 'bar', 'url': 'http://baz/'})
1439 1516

  
1440 1517
    app.reset()
1441 1518
    login(app)
1442 1519
    date = Booking.objects.all()[0].event.start_datetime
1443
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (
1444
        agenda.id, date.year, date.month, date.day))
1520
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
1445 1521
    assert resp.text.count('div class="booking') == 2
1446 1522
    assert 'hourspan-2' in resp.text  # table CSS class
1447 1523
    assert 'height: 50%; top: 0%;' in resp.text  # booking cells
......
1450 1526
    # (and visually this will give more room for events)
1451 1527
    meetingtype = MeetingType(agenda=agenda, label='Baz', duration=15)
1452 1528
    meetingtype.save()
1453
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (
1454
        agenda.id, date.year, date.month, date.day))
1529
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
1455 1530
    assert resp.text.count('div class="booking') == 2
1456 1531
    assert 'hourspan-4' in resp.text  # table CSS class
1457 1532

  
......
1464 1539

  
1465 1540
    app.reset()
1466 1541
    login(app)
1467
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (
1468
        agenda.id, date.year, date.month, date.day))
1542
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
1469 1543
    assert resp.text.count('div class="booking') == 1
1470 1544

  
1471 1545
    # wrong type
1472 1546
    agenda2 = Agenda(label=u'Foo bar')
1473 1547
    agenda2.save()
1474
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (
1475
        agenda2.id, date.year, date.month, date.day), status=404)
1548
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda2.id, date.year, date.month, date.day), status=404)
1476 1549

  
1477 1550
    # not enough permissions
1478 1551
    agenda2.view_role = manager_user.groups.all()[0]
1479 1552
    agenda2.save()
1480 1553
    app.reset()
1481 1554
    app = login(app, username='manager', password='manager')
1482
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (
1483
        agenda.id, date.year, date.month, date.day), status=403)
1555
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=403)
1484 1556

  
1485 1557
    # just enough permissions
1486 1558
    agenda.view_role = manager_user.groups.all()[0]
1487 1559
    agenda.save()
1488
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (
1489
        agenda.id, date.year, date.month, date.day), status=200)
1560
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200)
1561

  
1490 1562

  
1491 1563
def test_agenda_day_view_late_meeting(app, admin_user, manager_user, api_user):
1492 1564
    agenda = Agenda.objects.create(label='New Example', kind='meetings')
......
1498 1570

  
1499 1571
    today = datetime.date.today()
1500 1572

  
1501
    timeperiod = TimePeriod(desk=desk, weekday=today.weekday(),
1502
            start_time=datetime.time(10, 0),
1503
            end_time=datetime.time(23, 30))
1573
    timeperiod = TimePeriod(
1574
        desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(23, 30)
1575
    )
1504 1576
    timeperiod.save()
1505 1577

  
1506 1578
    login(app)
......
1508 1580
    assert resp.text.count('<tr') == 15
1509 1581
    assert '<th class="hour">11 p.m.</th>' in resp.text
1510 1582

  
1583

  
1511 1584
def test_agenda_invalid_day_view(app, admin_user, manager_user, api_user):
1512 1585
    agenda = Agenda.objects.create(label='New Example', kind='meetings')
1513 1586
    desk = Desk.objects.create(agenda=agenda, label='New Desk')
......
1520 1593
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, 2018, 11, 31), status=302)
1521 1594
    assert resp.location.endswith('2018/11/30/')
1522 1595

  
1596

  
1523 1597
def test_agenda_month_view(app, admin_user, manager_user, api_user):
1524 1598
    agenda = Agenda.objects.create(label='Passeports', kind='meetings')
1525 1599
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
......
1535 1609
    today = datetime.date.today()
1536 1610
    assert resp.request.url.endswith('%s/%s/' % (today.year, today.month))
1537 1611

  
1538
    assert 'Day view' in resp.text # date view link should be present
1612
    assert 'Day view' in resp.text  # date view link should be present
1539 1613
    assert 'No opening hours this month.' in resp.text
1540 1614

  
1541 1615
    today = datetime.date(2018, 11, 10)  # fixed day
1542 1616
    timeperiod_weekday = today.weekday()
1543
    timeperiod = TimePeriod(desk=desk, weekday=timeperiod_weekday,
1544
            start_time=datetime.time(10, 0),
1545
            end_time=datetime.time(18, 0))
1617
    timeperiod = TimePeriod(
1618
        desk=desk, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
1619
    )
1546 1620
    timeperiod.save()
1547 1621
    resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
1548 1622
    assert not 'No opening hours this month.' in resp.text
1549 1623
    assert not '<div class="booking' in resp.text
1550 1624
    first_month_day = today.replace(day=1)
1551
    last_month_day = today.replace(day=1, month=today.month+1) - datetime.timedelta(days=1)
1625
    last_month_day = today.replace(day=1, month=today.month + 1) - datetime.timedelta(days=1)
1552 1626
    start_week_number = first_month_day.isocalendar()[1]
1553 1627
    end_week_number = last_month_day.isocalendar()[1]
1554 1628
    weeks_number = end_week_number - start_week_number + 1
......
1564 1638
    booking_url = resp.json['data'][0]['api']['fillslot_url']
1565 1639
    booking_url2 = resp.json['data'][2]['api']['fillslot_url']
1566 1640
    booking = app.post(booking_url)
1567
    booking_2 = app.post_json(booking_url2,
1568
            params={'label': 'foo', 'user': 'bar', 'url': 'http://baz/'})
1641
    booking_2 = app.post_json(booking_url2, params={'label': 'foo', 'user': 'bar', 'url': 'http://baz/'})
1569 1642

  
1570 1643
    app.reset()
1571 1644
    login(app)
1572 1645
    date = Booking.objects.all()[0].event.start_datetime
1573
    resp = app.get('/manage/agendas/%s/%d/%d/' % (
1574
        agenda.id, date.year, date.month))
1575
    assert resp.text.count('<div class="booking" style="left:1.0%;height:33.0%;') == 2 # booking cells
1646
    resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
1647
    assert resp.text.count('<div class="booking" style="left:1.0%;height:33.0%;') == 2  # booking cells
1576 1648
    desk = Desk.objects.create(agenda=agenda, label='Desk B')
1577
    timeperiod = TimePeriod(desk=desk, weekday=timeperiod_weekday,
1578
            start_time=datetime.time(10, 0),
1579
            end_time=datetime.time(18, 0))
1649
    timeperiod = TimePeriod(
1650
        desk=desk, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
1651
    )
1580 1652
    timeperiod.save()
1581 1653

  
1582 1654
    app.reset()
......
1613 1685
    resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
1614 1686
    assert not 'No opening hours this month.' in resp.text
1615 1687

  
1688

  
1616 1689
def test_agenda_month_view_dst_change(app, admin_user, manager_user, api_user):
1617 1690
    agenda = Agenda.objects.create(label='Passeports', kind='meetings')
1618 1691
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
......
1621 1694
    meetingtype.save()
1622 1695

  
1623 1696
    for weekday in range(0, 7):  # open all mornings
1624
        TimePeriod(desk=desk, weekday=weekday,
1625
                start_time=datetime.time(9, 0),
1626
                end_time=datetime.time(12, 0)).save()
1697
        TimePeriod(
1698
            desk=desk, weekday=weekday, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
1699
        ).save()
1627 1700

  
1628 1701
    login(app)
1629 1702
    for date in ('2019-10-01', '2019-10-31'):
......
1658 1731
    resp = app.get('/manage/', status=200)
1659 1732
    resp = app.get('/manage/agendas/import/', status=403)
1660 1733

  
1734

  
1661 1735
def test_import_agenda(app, admin_user):
1662 1736
    agenda = Agenda(label=u'Foo bar')
1663 1737
    agenda.save()
1664
-