apply black 20.8b1 formatting
This commit is contained in:
parent
bfd2e97eb5
commit
eeda0c3f2e
|
@ -19,7 +19,9 @@ class Migration(migrations.Migration):
|
|||
('label', models.CharField(max_length=50, verbose_name='Label')),
|
||||
('slug', models.SlugField(verbose_name='Identifier')),
|
||||
],
|
||||
options={'ordering': ['label'],},
|
||||
options={
|
||||
'ordering': ['label'],
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -22,7 +22,9 @@ class Migration(migrations.Migration):
|
|||
('places', models.PositiveIntegerField(verbose_name='Places')),
|
||||
('agenda', models.ForeignKey(to='agendas.Agenda', on_delete=models.CASCADE)),
|
||||
],
|
||||
options={'ordering': ['agenda', 'start_datetime'],},
|
||||
options={
|
||||
'ordering': ['agenda', 'start_datetime'],
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -22,9 +22,15 @@ class Migration(migrations.Migration):
|
|||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='booking', name='in_waiting_list', field=models.BooleanField(default=False),
|
||||
model_name='booking',
|
||||
name='in_waiting_list',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='full',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(model_name='event', name='full', field=models.BooleanField(default=False),),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='waiting_list_places',
|
||||
|
|
|
@ -12,6 +12,8 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='agenda', name='label', field=models.CharField(max_length=100, verbose_name='Label'),
|
||||
model_name='agenda',
|
||||
name='label',
|
||||
field=models.CharField(max_length=100, verbose_name='Label'),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -29,7 +29,9 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
),
|
||||
],
|
||||
options={'ordering': ['label'],},
|
||||
options={
|
||||
'ordering': ['label'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TimePeriod',
|
||||
|
@ -56,7 +58,9 @@ class Migration(migrations.Migration):
|
|||
('start_time', models.TimeField(verbose_name='Start')),
|
||||
('end_time', models.TimeField(verbose_name='End')),
|
||||
],
|
||||
options={'ordering': ['weekday', 'start_time'],},
|
||||
options={
|
||||
'ordering': ['weekday', 'start_time'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='agenda',
|
||||
|
|
|
@ -13,7 +13,8 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='event', options={'ordering': ['agenda', 'start_datetime', 'label']},
|
||||
name='event',
|
||||
options={'ordering': ['agenda', 'start_datetime', 'label']},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='agenda',
|
||||
|
|
|
@ -12,6 +12,8 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='meetingtype', name='slug', field=models.SlugField(verbose_name='Identifier'),
|
||||
model_name='meetingtype',
|
||||
name='slug',
|
||||
field=models.SlugField(verbose_name='Identifier'),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -12,7 +12,9 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='agenda', name='label', field=models.CharField(max_length=150, verbose_name='Label'),
|
||||
model_name='agenda',
|
||||
name='label',
|
||||
field=models.CharField(max_length=150, verbose_name='Label'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
|
|
|
@ -22,6 +22,8 @@ class Migration(migrations.Migration):
|
|||
('slug', models.SlugField(max_length=150, verbose_name='Identifier')),
|
||||
('agenda', models.ForeignKey(to='agendas.Agenda', on_delete=models.CASCADE)),
|
||||
],
|
||||
options={'ordering': ['label'],},
|
||||
options={
|
||||
'ordering': ['label'],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -37,5 +37,8 @@ class Migration(migrations.Migration):
|
|||
name='desk',
|
||||
field=models.ForeignKey(to='agendas.Desk', on_delete=models.CASCADE),
|
||||
),
|
||||
migrations.RemoveField(model_name='timeperiod', name='agenda',),
|
||||
migrations.RemoveField(
|
||||
model_name='timeperiod',
|
||||
name='agenda',
|
||||
),
|
||||
]
|
||||
|
|
|
@ -26,6 +26,8 @@ class Migration(migrations.Migration):
|
|||
('end_datetime', models.DateTimeField(verbose_name='Exception end time')),
|
||||
('desk', models.ForeignKey(to='agendas.Desk', on_delete=models.CASCADE)),
|
||||
],
|
||||
options={'ordering': ['start_datetime'],},
|
||||
options={
|
||||
'ordering': ['start_datetime'],
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -11,11 +11,19 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(model_name='booking', name='backoffice_url', field=models.URLField(blank=True),),
|
||||
migrations.AddField(
|
||||
model_name='booking', name='label', field=models.CharField(max_length=250, blank=True),
|
||||
model_name='booking',
|
||||
name='backoffice_url',
|
||||
field=models.URLField(blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='booking', name='user_name', field=models.CharField(max_length=250, blank=True),
|
||||
model_name='booking',
|
||||
name='label',
|
||||
field=models.CharField(max_length=250, blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='booking',
|
||||
name='user_name',
|
||||
field=models.CharField(max_length=250, blank=True),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -17,7 +17,9 @@ class Migration(migrations.Migration):
|
|||
field=models.SlugField(max_length=160, verbose_name='Identifier'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='desk', name='slug', field=models.SlugField(max_length=160, verbose_name='Identifier'),
|
||||
model_name='desk',
|
||||
name='slug',
|
||||
field=models.SlugField(max_length=160, verbose_name='Identifier'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='meetingtype',
|
||||
|
|
|
@ -12,7 +12,10 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(name='meetingtype', options={'ordering': ['duration', 'label']},),
|
||||
migrations.AlterModelOptions(
|
||||
name='meetingtype',
|
||||
options={'ordering': ['duration', 'label']},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='timeperiodexception',
|
||||
name='recurrence_id',
|
||||
|
|
|
@ -17,6 +17,12 @@ class Migration(migrations.Migration):
|
|||
name='slug',
|
||||
field=models.SlugField(max_length=160, unique=True, verbose_name='Identifier'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(name='desk', unique_together=set([('agenda', 'slug')]),),
|
||||
migrations.AlterUniqueTogether(name='meetingtype', unique_together=set([('agenda', 'slug')]),),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='desk',
|
||||
unique_together=set([('agenda', 'slug')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='meetingtype',
|
||||
unique_together=set([('agenda', 'slug')]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -19,5 +19,8 @@ class Migration(migrations.Migration):
|
|||
default=None, null=True, blank=True, max_length=160, verbose_name='Identifier'
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(name='event', unique_together=set([('agenda', 'slug')]),),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='event',
|
||||
unique_together=set([('agenda', 'slug')]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -12,6 +12,12 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(model_name='desk', name='timeperiod_exceptions_remote_url',),
|
||||
migrations.RemoveField(model_name='timeperiodexception', name='external_id',),
|
||||
migrations.RemoveField(
|
||||
model_name='desk',
|
||||
name='timeperiod_exceptions_remote_url',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='timeperiodexception',
|
||||
name='external_id',
|
||||
),
|
||||
]
|
||||
|
|
|
@ -11,6 +11,7 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='virtualmember', unique_together={('virtual_agenda', 'real_agenda')},
|
||||
name='virtualmember',
|
||||
unique_together={('virtual_agenda', 'real_agenda')},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -12,6 +12,8 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='booking', name='user_external_id', field=models.CharField(blank=True, max_length=250),
|
||||
model_name='booking',
|
||||
name='user_external_id',
|
||||
field=models.CharField(blank=True, max_length=250),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -30,9 +30,13 @@ class Migration(migrations.Migration):
|
|||
options={'ordering': ['label']},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='agenda', name='resources', field=models.ManyToManyField(to='agendas.Resource'),
|
||||
model_name='agenda',
|
||||
name='resources',
|
||||
field=models.ManyToManyField(to='agendas.Resource'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event', name='resources', field=models.ManyToManyField(to='agendas.Resource'),
|
||||
model_name='event',
|
||||
name='resources',
|
||||
field=models.ManyToManyField(to='agendas.Resource'),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -13,7 +13,8 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='event', options={'ordering': ['agenda', 'start_datetime', 'duration', 'label']},
|
||||
name='event',
|
||||
options={'ordering': ['agenda', 'start_datetime', 'duration', 'label']},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
|
|
|
@ -22,7 +22,9 @@ class Migration(migrations.Migration):
|
|||
('slug', models.SlugField(max_length=160, unique=True, verbose_name='Identifier')),
|
||||
('label', models.CharField(max_length=150, verbose_name='Label')),
|
||||
],
|
||||
options={'ordering': ['label'],},
|
||||
options={
|
||||
'ordering': ['label'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='agenda',
|
||||
|
|
|
@ -13,6 +13,8 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='booking', name='cancel_callback_url', field=models.URLField(blank=True),
|
||||
model_name='booking',
|
||||
name='cancel_callback_url',
|
||||
field=models.URLField(blank=True),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -26,12 +26,22 @@ class Migration(migrations.Migration):
|
|||
('booking_errors', jsonfield.fields.JSONField(default=dict)),
|
||||
('bookings', models.ManyToManyField(to='agendas.Booking')),
|
||||
],
|
||||
options={'ordering': ['-timestamp'],},
|
||||
options={
|
||||
'ordering': ['-timestamp'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event', name='cancellation_scheduled', field=models.BooleanField(default=False),
|
||||
model_name='event',
|
||||
name='cancellation_scheduled',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='cancelled',
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
),
|
||||
),
|
||||
migrations.AddField(model_name='event', name='cancelled', field=models.BooleanField(default=False,),),
|
||||
migrations.AddField(
|
||||
model_name='eventcancellationreport',
|
||||
name='event',
|
||||
|
|
|
@ -13,7 +13,9 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='timeperiodexceptionsource', name='enabled', field=models.BooleanField(default=True),
|
||||
model_name='timeperiodexceptionsource',
|
||||
name='enabled',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='timeperiodexceptionsource',
|
||||
|
|
|
@ -13,6 +13,8 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event', name='almost_full', field=models.BooleanField(default=False),
|
||||
model_name='event',
|
||||
name='almost_full',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -12,11 +12,19 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(model_name='booking', name='form_url', field=models.URLField(blank=True),),
|
||||
migrations.AddField(
|
||||
model_name='booking', name='user_email', field=models.EmailField(blank=True, max_length=254),
|
||||
model_name='booking',
|
||||
name='form_url',
|
||||
field=models.URLField(blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='booking', name='user_phone_number', field=models.CharField(blank=True, max_length=16),
|
||||
model_name='booking',
|
||||
name='user_email',
|
||||
field=models.EmailField(blank=True, max_length=254),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='booking',
|
||||
name='user_phone_number',
|
||||
field=models.CharField(blank=True, max_length=16),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -63,6 +63,8 @@ class Migration(migrations.Migration):
|
|||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='booking', name='reminder_datetime', field=models.DateTimeField(null=True),
|
||||
model_name='booking',
|
||||
name='reminder_datetime',
|
||||
field=models.DateTimeField(null=True),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -29,6 +29,8 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='booking', name='anonymization_datetime', field=models.DateTimeField(null=True),
|
||||
model_name='booking',
|
||||
name='anonymization_datetime',
|
||||
field=models.DateTimeField(null=True),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -49,7 +49,9 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
),
|
||||
],
|
||||
options={'ordering': ['label'],},
|
||||
options={
|
||||
'ordering': ['label'],
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='timeperiodexception',
|
||||
|
|
|
@ -13,6 +13,7 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='timeperiodexceptionsource', unique_together=set([('desk', 'settings_slug')]),
|
||||
name='timeperiodexceptionsource',
|
||||
unique_together=set([('desk', 'settings_slug')]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -12,5 +12,8 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(model_name='timeperiodexception', name='external',),
|
||||
migrations.RemoveField(
|
||||
model_name='timeperiodexception',
|
||||
name='external',
|
||||
),
|
||||
]
|
||||
|
|
|
@ -31,7 +31,9 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
),
|
||||
],
|
||||
options={'ordering': ('pk',),},
|
||||
options={
|
||||
'ordering': ('pk',),
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='booking',
|
||||
|
@ -43,5 +45,8 @@ class Migration(migrations.Migration):
|
|||
to='agendas.BookingColor',
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(name='bookingcolor', unique_together=set([('agenda', 'label')]),),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='bookingcolor',
|
||||
unique_together=set([('agenda', 'label')]),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -242,7 +242,7 @@ class Agenda(models.Model):
|
|||
return [self]
|
||||
|
||||
def iter_meetingtypes(self, excluded_agenda=None):
|
||||
""" Expose agenda's meetingtypes.
|
||||
"""Expose agenda's meetingtypes.
|
||||
straighforward on a real agenda
|
||||
On a virtual agenda we expose transient meeting types based on on the
|
||||
the real ones shared by every real agendas.
|
||||
|
@ -435,12 +435,12 @@ class Agenda(models.Model):
|
|||
return new_agenda
|
||||
|
||||
def get_effective_time_periods(self):
|
||||
'''Regroup timeperiods by desks.
|
||||
"""Regroup timeperiods by desks.
|
||||
|
||||
List all timeperiods, timeperiods having the same begin_time and
|
||||
end_time are regrouped in a SharedTimePeriod object, which has a
|
||||
list of desks instead of only one desk.
|
||||
'''
|
||||
List all timeperiods, timeperiods having the same begin_time and
|
||||
end_time are regrouped in a SharedTimePeriod object, which has a
|
||||
list of desks instead of only one desk.
|
||||
"""
|
||||
if self.kind == 'virtual':
|
||||
return self.get_effective_time_periods_virtual()
|
||||
elif self.kind == 'meetings':
|
||||
|
@ -449,13 +449,14 @@ class Agenda(models.Model):
|
|||
raise ValueError('does not work with kind %r' % self.kind)
|
||||
|
||||
def get_effective_time_periods_meetings(self):
|
||||
'''List timeperiod instances for all desks of the agenda, convert them
|
||||
into an Interval of WeekTime which can be compared and regrouped using
|
||||
itertools.groupby.
|
||||
'''
|
||||
"""List timeperiod instances for all desks of the agenda, convert them
|
||||
into an Interval of WeekTime which can be compared and regrouped using
|
||||
itertools.groupby.
|
||||
"""
|
||||
yield from (
|
||||
SharedTimePeriod.from_weektime_interval(
|
||||
weektime_interval, desks=[time_period.desk for time_period in time_periods],
|
||||
weektime_interval,
|
||||
desks=[time_period.desk for time_period in time_periods],
|
||||
)
|
||||
for weektime_interval, time_periods in itertools.groupby(
|
||||
TimePeriod.objects.filter(desk__agenda=self)
|
||||
|
@ -466,10 +467,10 @@ class Agenda(models.Model):
|
|||
)
|
||||
|
||||
def get_effective_time_periods_virtual(self):
|
||||
'''List timeperiod instances for all desks of all real agendas of this
|
||||
virtual agenda, convert them into an Interval of WeekTime which can be
|
||||
compared and regrouped using itertools.groupby.
|
||||
'''
|
||||
"""List timeperiod instances for all desks of all real agendas of this
|
||||
virtual agenda, convert them into an Interval of WeekTime which can be
|
||||
compared and regrouped using itertools.groupby.
|
||||
"""
|
||||
closed_hours_by_days = IntervalSet.from_ordered(
|
||||
[
|
||||
time_period.as_weektime_interval()
|
||||
|
@ -557,12 +558,12 @@ class Agenda(models.Model):
|
|||
|
||||
|
||||
class VirtualMember(models.Model):
|
||||
'''Trough model to link virtual agendas to their real agendas.
|
||||
"""Trough model to link virtual agendas to their real agendas.
|
||||
|
||||
Real agendas linked to a virtual agenda MUST all have the same list of
|
||||
MeetingType based on their label, slug and duration. It's enforced by
|
||||
VirtualMember.clean() and the realted management views.
|
||||
'''
|
||||
Real agendas linked to a virtual agenda MUST all have the same list of
|
||||
MeetingType based on their label, slug and duration. It's enforced by
|
||||
VirtualMember.clean() and the realted management views.
|
||||
"""
|
||||
|
||||
virtual_agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE, related_name='real_members')
|
||||
real_agenda = models.ForeignKey(
|
||||
|
@ -602,11 +603,13 @@ WEEKDAYS_LIST = sorted(WEEKDAYS.items(), key=lambda x: x[0])
|
|||
|
||||
|
||||
class WeekTime(collections.namedtuple('WeekTime', ['weekday', 'time'])):
|
||||
'''Representation of a time point in a weekday, ex.: Monday at 5 o'clock.
|
||||
'''
|
||||
"""Representation of a time point in a weekday, ex.: Monday at 5 o'clock."""
|
||||
|
||||
def __repr__(self):
|
||||
return '%s / %s' % (force_text(WEEKDAYS[self.weekday]), date_format(self.time, 'TIME_FORMAT'),)
|
||||
return '%s / %s' % (
|
||||
force_text(WEEKDAYS[self.weekday]),
|
||||
date_format(self.time, 'TIME_FORMAT'),
|
||||
)
|
||||
|
||||
|
||||
class TimePeriod(models.Model):
|
||||
|
@ -663,34 +666,40 @@ class TimePeriod(models.Model):
|
|||
return new_period
|
||||
|
||||
def as_weektime_interval(self):
|
||||
return Interval(WeekTime(self.weekday, self.start_time), WeekTime(self.weekday, self.end_time),)
|
||||
return Interval(
|
||||
WeekTime(self.weekday, self.start_time),
|
||||
WeekTime(self.weekday, self.end_time),
|
||||
)
|
||||
|
||||
def as_shared_timeperiods(self):
|
||||
return SharedTimePeriod(
|
||||
weekday=self.weekday, start_time=self.start_time, end_time=self.end_time, desks=[self.desk],
|
||||
weekday=self.weekday,
|
||||
start_time=self.start_time,
|
||||
end_time=self.end_time,
|
||||
desks=[self.desk],
|
||||
)
|
||||
|
||||
|
||||
@functools.total_ordering
|
||||
class SharedTimePeriod(object):
|
||||
'''
|
||||
Hold common timeperiod for multiple desks.
|
||||
"""
|
||||
Hold common timeperiod for multiple desks.
|
||||
|
||||
To improve performance when generating meetings slots for virtual
|
||||
agendas or agendas with many desks, we deduplicate time-periods between
|
||||
all desks of all agendas.
|
||||
To improve performance when generating meetings slots for virtual
|
||||
agendas or agendas with many desks, we deduplicate time-periods between
|
||||
all desks of all agendas.
|
||||
|
||||
Deduplication is based on a common key, and implemented through __eq__
|
||||
and __lt__ which will be used by itertools.groupby().
|
||||
Deduplication is based on a common key, and implemented through __eq__
|
||||
and __lt__ which will be used by itertools.groupby().
|
||||
|
||||
(weekday, start_datetime, end_datetime)
|
||||
(weekday, start_datetime, end_datetime)
|
||||
|
||||
it's done in the deduplicate() classmethod.
|
||||
it's done in the deduplicate() classmethod.
|
||||
|
||||
At the level of gel_all_slots() timeperiod are re-duplicated if the
|
||||
min_datetime,max_datetime of the desk's agendas differs (see the code
|
||||
of get_all_slots() for details).
|
||||
'''
|
||||
At the level of gel_all_slots() timeperiod are re-duplicated if the
|
||||
min_datetime,max_datetime of the desk's agendas differs (see the code
|
||||
of get_all_slots() for details).
|
||||
"""
|
||||
|
||||
__slots__ = ['weekday', 'start_time', 'end_time', 'desks']
|
||||
|
||||
|
@ -722,27 +731,27 @@ class SharedTimePeriod(object):
|
|||
)
|
||||
|
||||
def get_time_slots(self, min_datetime, max_datetime, meeting_duration, base_duration):
|
||||
'''Generate all possible time slots between min_datetime and max_datime
|
||||
of duration meeting_duration minutes and spaced by base_duration
|
||||
minutes, i.e.
|
||||
"""Generate all possible time slots between min_datetime and max_datime
|
||||
of duration meeting_duration minutes and spaced by base_duration
|
||||
minutes, i.e.
|
||||
|
||||
compute a list [a,b] -> [c,d] -> ...
|
||||
where b-a = meeting_duration and c-a = base_duration.
|
||||
compute a list [a,b] -> [c,d] -> ...
|
||||
where b-a = meeting_duration and c-a = base_duration.
|
||||
|
||||
We start with the first time following min_datetime and being on
|
||||
the same weekday of the current period.
|
||||
We start with the first time following min_datetime and being on
|
||||
the same weekday of the current period.
|
||||
|
||||
Then we iterate, advancing by base_duration minutes each time.
|
||||
Then we iterate, advancing by base_duration minutes each time.
|
||||
|
||||
If we cross the end_time of the period or end of the current_day
|
||||
(means end_time is midnight), it advance time to self.start_time on
|
||||
the next week (same weekday, same start, one week in the future).
|
||||
If we cross the end_time of the period or end of the current_day
|
||||
(means end_time is midnight), it advance time to self.start_time on
|
||||
the next week (same weekday, same start, one week in the future).
|
||||
|
||||
When it crosses end_datetime it stops.
|
||||
When it crosses end_datetime it stops.
|
||||
|
||||
Generated start_datetime MUST be in the local timezone as the API
|
||||
needs it to generate stable ids.
|
||||
'''
|
||||
Generated start_datetime MUST be in the local timezone as the API
|
||||
needs it to generate stable ids.
|
||||
"""
|
||||
meeting_duration = datetime.timedelta(minutes=meeting_duration)
|
||||
duration = datetime.timedelta(minutes=base_duration)
|
||||
|
||||
|
@ -784,7 +793,12 @@ class SharedTimePeriod(object):
|
|||
def from_weektime_interval(cls, weektime_interval, desks=()):
|
||||
begin, end = weektime_interval
|
||||
assert begin.weekday == end.weekday
|
||||
return cls(weekday=begin.weekday, start_time=begin.time, end_time=end.time, desks=desks,)
|
||||
return cls(
|
||||
weekday=begin.weekday,
|
||||
start_time=begin.time,
|
||||
end_time=end.time,
|
||||
desks=desks,
|
||||
)
|
||||
|
||||
|
||||
class MeetingType(models.Model):
|
||||
|
|
|
@ -92,24 +92,24 @@ TimeSlot = collections.namedtuple('TimeSlot', ['start_datetime', 'end_datetime',
|
|||
def get_all_slots(
|
||||
base_agenda, meeting_type, resources=None, unique=False, start_datetime=None, end_datetime=None
|
||||
):
|
||||
'''Get all occupation state of all possible slots for the given agenda (of
|
||||
its real agendas for a virtual agenda) and the given meeting_type.
|
||||
"""Get all occupation state of all possible slots for the given agenda (of
|
||||
its real agendas for a virtual agenda) and the given meeting_type.
|
||||
|
||||
The process is done in four phases:
|
||||
- first phase: aggregate time intervals, during which a meeting is impossible
|
||||
due to TimePeriodException models, by desk in IntervalSet (compressed
|
||||
and ordered list of intervals).
|
||||
- second phase: aggregate time intervals by desk for already booked slots, again
|
||||
to make IntervalSet,
|
||||
- third phase: for a meetings agenda, if resources has to be booked,
|
||||
aggregate time intervals for already booked resources, to make IntervalSet.
|
||||
- fourth and last phase: generate time slots from each time period based
|
||||
on the time period definition and on the desk's respective agenda real
|
||||
min/max_datetime; for each time slot check its status in the exclusion
|
||||
and bookings sets.
|
||||
If it is excluded, ignore it completely.
|
||||
It if is booked, report the slot as full.
|
||||
'''
|
||||
The process is done in four phases:
|
||||
- first phase: aggregate time intervals, during which a meeting is impossible
|
||||
due to TimePeriodException models, by desk in IntervalSet (compressed
|
||||
and ordered list of intervals).
|
||||
- second phase: aggregate time intervals by desk for already booked slots, again
|
||||
to make IntervalSet,
|
||||
- third phase: for a meetings agenda, if resources has to be booked,
|
||||
aggregate time intervals for already booked resources, to make IntervalSet.
|
||||
- fourth and last phase: generate time slots from each time period based
|
||||
on the time period definition and on the desk's respective agenda real
|
||||
min/max_datetime; for each time slot check its status in the exclusion
|
||||
and bookings sets.
|
||||
If it is excluded, ignore it completely.
|
||||
It if is booked, report the slot as full.
|
||||
"""
|
||||
resources = resources or []
|
||||
# virtual agendas have one constraint :
|
||||
# all the real agendas MUST have the same meetingstypes, the consequence is
|
||||
|
@ -383,7 +383,8 @@ def get_event_detail(request, event, agenda=None):
|
|||
),
|
||||
'fillslot_url': request.build_absolute_uri(
|
||||
reverse(
|
||||
'api-fillslot', kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
'api-fillslot',
|
||||
kwargs={'agenda_identifier': agenda.slug, 'event_identifier': event.slug},
|
||||
)
|
||||
),
|
||||
'status_url': request.build_absolute_uri(
|
||||
|
@ -433,7 +434,11 @@ class Agendas(APIView):
|
|||
start_datetime__gte=localtime(now()),
|
||||
).order_by()
|
||||
agendas_queryset = agendas_queryset.filter(kind='events').prefetch_related(
|
||||
Prefetch('event_set', queryset=event_queryset, to_attr='prefetched_events',)
|
||||
Prefetch(
|
||||
'event_set',
|
||||
queryset=event_queryset,
|
||||
to_attr='prefetched_events',
|
||||
)
|
||||
)
|
||||
|
||||
agendas = []
|
||||
|
@ -452,9 +457,9 @@ agendas = Agendas.as_view()
|
|||
|
||||
|
||||
class AgendaDetail(APIView):
|
||||
'''
|
||||
"""
|
||||
Retrieve an agenda instance.
|
||||
'''
|
||||
"""
|
||||
|
||||
permission_classes = ()
|
||||
|
||||
|
@ -599,14 +604,17 @@ class MeetingDatetimes(APIView):
|
|||
fillslot_url += '?resources=%s' % ','.join(r.slug for r in resources)
|
||||
|
||||
def make_id(start_datetime, meeting_type):
|
||||
'''Make virtual id for a slot, combining meeting_type.id and
|
||||
iso-format of date and time.
|
||||
!!! The datetime must always be in the local timezone and the local
|
||||
timezone must not change if we want the id to be stable.
|
||||
It MUST be a garanty of SharedTimePeriod.get_time_slots(),
|
||||
!!!
|
||||
'''
|
||||
return '%s:%s' % (meeting_type.slug, start_datetime.strftime('%Y-%m-%d-%H%M'),)
|
||||
"""Make virtual id for a slot, combining meeting_type.id and
|
||||
iso-format of date and time.
|
||||
!!! The datetime must always be in the local timezone and the local
|
||||
timezone must not change if we want the id to be stable.
|
||||
It MUST be a garanty of SharedTimePeriod.get_time_slots(),
|
||||
!!!
|
||||
"""
|
||||
return '%s:%s' % (
|
||||
meeting_type.slug,
|
||||
start_datetime.strftime('%Y-%m-%d-%H%M'),
|
||||
)
|
||||
|
||||
response = {
|
||||
'data': [
|
||||
|
@ -722,9 +730,9 @@ agenda_desk_list = AgendaDeskList.as_view()
|
|||
|
||||
|
||||
class SlotSerializer(serializers.Serializer):
|
||||
'''
|
||||
"""
|
||||
payload to fill one slot. The slot (event id) is in the URL.
|
||||
'''
|
||||
"""
|
||||
|
||||
label = serializers.CharField(max_length=250, allow_blank=True)
|
||||
user_external_id = serializers.CharField(max_length=250, allow_blank=True)
|
||||
|
@ -749,10 +757,10 @@ class StringOrListField(serializers.ListField):
|
|||
|
||||
|
||||
class SlotsSerializer(SlotSerializer):
|
||||
'''
|
||||
"""
|
||||
payload to fill multiple slots: same as SlotSerializer, but the
|
||||
slots list is in the payload.
|
||||
'''
|
||||
"""
|
||||
|
||||
slots = StringOrListField(required=True, child=serializers.CharField(max_length=160, allow_blank=False))
|
||||
|
||||
|
@ -968,7 +976,8 @@ class Fillslots(APIView):
|
|||
|
||||
if available_desk is None:
|
||||
raise APIError(
|
||||
_('no more desk available'), err_class='no more desk available',
|
||||
_('no more desk available'),
|
||||
err_class='no more desk available',
|
||||
)
|
||||
|
||||
# all datetimes are free, book them in order
|
||||
|
@ -1019,7 +1028,8 @@ class Fillslots(APIView):
|
|||
for event in events:
|
||||
if payload.get('force_waiting_list') and not event.waiting_list_places:
|
||||
raise APIError(
|
||||
_('no waiting list'), err_class='no waiting list',
|
||||
_('no waiting list'),
|
||||
err_class='no waiting list',
|
||||
)
|
||||
|
||||
if event.waiting_list_places:
|
||||
|
@ -1033,12 +1043,14 @@ class Fillslots(APIView):
|
|||
in_waiting_list = True
|
||||
if (event.waiting_list + places_count) > event.waiting_list_places:
|
||||
raise APIError(
|
||||
_('sold out'), err_class='sold out',
|
||||
_('sold out'),
|
||||
err_class='sold out',
|
||||
)
|
||||
else:
|
||||
if (event.booked_places + places_count) > event.places:
|
||||
raise APIError(
|
||||
_('sold out'), err_class='sold out',
|
||||
_('sold out'),
|
||||
err_class='sold out',
|
||||
)
|
||||
|
||||
with transaction.atomic():
|
||||
|
@ -1167,12 +1179,12 @@ booking = BookingAPI.as_view()
|
|||
|
||||
|
||||
class CancelBooking(APIView):
|
||||
'''
|
||||
"""
|
||||
Cancel a booking.
|
||||
|
||||
It will return error codes if the booking was cancelled before (code 1) or
|
||||
if the booking is not primary (code 2).
|
||||
'''
|
||||
"""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
|
@ -1201,13 +1213,13 @@ cancel_booking = CancelBooking.as_view()
|
|||
|
||||
|
||||
class AcceptBooking(APIView):
|
||||
'''
|
||||
"""
|
||||
Accept a booking currently in the waiting list.
|
||||
|
||||
It will return error codes if the booking was cancelled before (code 1),
|
||||
if the booking is not primary (code 2) or
|
||||
if the booking was not in waiting list (code 3).
|
||||
'''
|
||||
"""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
|
@ -1248,13 +1260,13 @@ accept_booking = AcceptBooking.as_view()
|
|||
|
||||
|
||||
class SuspendBooking(APIView):
|
||||
'''
|
||||
"""
|
||||
Suspend a accepted booking.
|
||||
|
||||
It will return error codes if the booking was cancelled before (code 1)
|
||||
if the booking is not primary (code 2) or
|
||||
if the booking is already in waiting list (code 3).
|
||||
'''
|
||||
"""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
|
@ -1294,14 +1306,14 @@ class ResizeSerializer(serializers.Serializer):
|
|||
|
||||
|
||||
class ResizeBooking(APIView):
|
||||
'''
|
||||
"""
|
||||
Resize a booking.
|
||||
|
||||
It will return error codes if the booking was cancelled before (code 1)
|
||||
if the booking is not primary (code 2)
|
||||
if the event is sold out (code 3) or
|
||||
if the booking is on multi events (code 4).
|
||||
'''
|
||||
"""
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
serializer_class = ResizeSerializer
|
||||
|
@ -1399,7 +1411,12 @@ class ResizeBooking(APIView):
|
|||
with transaction.atomic():
|
||||
bulk_bookings = []
|
||||
for i in range(0, primary_wanted_places - primary_booked_places):
|
||||
bulk_bookings.append(booking.clone(primary_booking=booking, save=False,))
|
||||
bulk_bookings.append(
|
||||
booking.clone(
|
||||
primary_booking=booking,
|
||||
save=False,
|
||||
)
|
||||
)
|
||||
Booking.objects.bulk_create(bulk_bookings)
|
||||
|
||||
return self.success(booking)
|
||||
|
|
|
@ -21,30 +21,30 @@ Interval = collections.namedtuple('Interval', ['begin', 'end'])
|
|||
|
||||
|
||||
class IntervalSet(object):
|
||||
'''Store a set made of an union of disjoint open/closed intervals (it
|
||||
currently does not really care about their openness), i.e.
|
||||
"""Store a set made of an union of disjoint open/closed intervals (it
|
||||
currently does not really care about their openness), i.e.
|
||||
|
||||
S = [a[0], b[0]] ∪ ∪ [a[n-1], b[n-1]]
|
||||
S = [a[0], b[0]] ∪ ∪ [a[n-1], b[n-1]]
|
||||
|
||||
where
|
||||
where
|
||||
|
||||
forall i < n. a_i < b_i
|
||||
forall i < (n-1). b_i < a[i+1]
|
||||
'''
|
||||
forall i < n. a_i < b_i
|
||||
forall i < (n-1). b_i < a[i+1]
|
||||
"""
|
||||
|
||||
__slots__ = ['begin', 'end']
|
||||
|
||||
def __init__(self, iterable=(), already_sorted=False):
|
||||
'''
|
||||
Initialize a new IntervalSet from a list of Interval or 2-tuple.
|
||||
"""
|
||||
Initialize a new IntervalSet from a list of Interval or 2-tuple.
|
||||
|
||||
Iterable will be sorted, if it's already sorted use the
|
||||
from_ordered() classmethod.
|
||||
Iterable will be sorted, if it's already sorted use the
|
||||
from_ordered() classmethod.
|
||||
|
||||
It's faster than using add() because intervals are merged as we
|
||||
traverse the list, and self.begin and self.end are built in O(n)
|
||||
time where len(iterable) = n.
|
||||
'''
|
||||
It's faster than using add() because intervals are merged as we
|
||||
traverse the list, and self.begin and self.end are built in O(n)
|
||||
time where len(iterable) = n.
|
||||
"""
|
||||
if not already_sorted:
|
||||
iterable = sorted(iterable)
|
||||
self.begin = []
|
||||
|
@ -81,11 +81,11 @@ class IntervalSet(object):
|
|||
return begin, end
|
||||
|
||||
def add(self, begin, end=Ellipsis):
|
||||
'''Add a new interval to the set, eventually merging it with actual
|
||||
ones.
|
||||
"""Add a new interval to the set, eventually merging it with actual
|
||||
ones.
|
||||
|
||||
It uses bisect_left() to maintaint the ordering of intervals.
|
||||
'''
|
||||
It uses bisect_left() to maintaint the ordering of intervals.
|
||||
"""
|
||||
begin, end = self._begin_or_interval(begin, end)
|
||||
# insert an interval by merging with previous and following intervals
|
||||
# if they overlap
|
||||
|
@ -120,10 +120,10 @@ class IntervalSet(object):
|
|||
self.end.insert(i, end)
|
||||
|
||||
def overlaps(self, begin, end=Ellipsis):
|
||||
'''
|
||||
Find if the [begin, end] has a non-empty (or closed) overlaps with
|
||||
one of the contained intervals.
|
||||
'''
|
||||
"""
|
||||
Find if the [begin, end] has a non-empty (or closed) overlaps with
|
||||
one of the contained intervals.
|
||||
"""
|
||||
begin, end = self._begin_or_interval(begin, end)
|
||||
# look for non-zero size overlap
|
||||
i = bisect.bisect_left(self.begin, begin)
|
||||
|
@ -134,9 +134,9 @@ class IntervalSet(object):
|
|||
return False
|
||||
|
||||
def __iter__(self):
|
||||
'''
|
||||
Generate the ordered list of included intervals as 2-tuples.
|
||||
'''
|
||||
"""
|
||||
Generate the ordered list of included intervals as 2-tuples.
|
||||
"""
|
||||
return map(Interval._make, zip(self.begin, self.end))
|
||||
|
||||
def __eq__(self, other):
|
||||
|
|
|
@ -273,7 +273,9 @@ class TimePeriodForm(forms.ModelForm):
|
|||
|
||||
class NewDeskForm(forms.ModelForm):
|
||||
copy_from = forms.ModelChoiceField(
|
||||
label=_('Copy settings of desk'), required=False, queryset=Desk.objects.none(),
|
||||
label=_('Copy settings of desk'),
|
||||
required=False,
|
||||
queryset=Desk.objects.none(),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -422,7 +422,8 @@ class ResourceMonthView(DateMixin, MonthArchiveView):
|
|||
|
||||
for week_number in range(first_week_number + first_week_offset, last_week_number + 1):
|
||||
yield self.get_week_timetable_infos(
|
||||
week_number - first_week_number, week_end_offset=int(hide_sunday) + int(hide_weekend),
|
||||
week_number - first_week_number,
|
||||
week_end_offset=int(hide_sunday) + int(hide_weekend),
|
||||
)
|
||||
|
||||
def get_week_timetable_infos(self, week_index, week_end_offset=0):
|
||||
|
@ -1039,7 +1040,8 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
|
|||
context = super(AgendaMonthView, self).get_context_data(**kwargs)
|
||||
if self.agenda.kind == 'events':
|
||||
context['cancellation_reports'] = EventCancellationReport.objects.filter(
|
||||
event__agenda=self.agenda, seen=False,
|
||||
event__agenda=self.agenda,
|
||||
seen=False,
|
||||
).all()
|
||||
else:
|
||||
context['single_desk'] = bool(len(self.agenda.prefetched_desks) == 1)
|
||||
|
@ -1110,7 +1112,8 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
|
|||
|
||||
for week_number in range(first_week_number + first_week_offset, last_week_number + 1):
|
||||
yield self.get_week_timetable_infos(
|
||||
week_number - first_week_number, week_end_offset=int(hide_sunday) + int(hide_weekend),
|
||||
week_number - first_week_number,
|
||||
week_end_offset=int(hide_sunday) + int(hide_weekend),
|
||||
)
|
||||
|
||||
def get_week_timetable_infos(self, week_index, week_end_offset=0):
|
||||
|
@ -1999,7 +2002,11 @@ class AgendaAddTimePeriodExceptionView(ManagedDeskMixin, CreateView):
|
|||
end_datetime=self.object.end_datetime,
|
||||
)
|
||||
)
|
||||
message = ungettext('Exception added.', 'Exceptions added.', len(exceptions),)
|
||||
message = ungettext(
|
||||
'Exception added.',
|
||||
'Exceptions added.',
|
||||
len(exceptions),
|
||||
)
|
||||
messages.info(self.request, message)
|
||||
for exception in exceptions:
|
||||
if exception.has_booking_within_time_slot():
|
||||
|
|
|
@ -79,7 +79,10 @@ WSGI_APPLICATION = 'chrono.wsgi.application'
|
|||
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),}
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
# Internationalization
|
||||
|
|
|
@ -41,7 +41,9 @@ if 'mellon' in settings.INSTALLED_APPS:
|
|||
if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS:
|
||||
import debug_toolbar
|
||||
|
||||
urlpatterns = [url(r'^__debug__/', include(debug_toolbar.urls)),] + urlpatterns
|
||||
urlpatterns = [
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
] + urlpatterns
|
||||
|
||||
# static and media files
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
|
6
setup.py
6
setup.py
|
@ -30,9 +30,9 @@ class eo_sdist(sdist):
|
|||
|
||||
|
||||
def get_version():
|
||||
'''Use the VERSION, if absent generates a version with git describe, if not
|
||||
tag exists, take 0.0- and add the length of the commit log.
|
||||
'''
|
||||
"""Use the VERSION, if absent generates a version with git describe, if not
|
||||
tag exists, take 0.0- and add the length of the commit log.
|
||||
"""
|
||||
if os.path.exists('VERSION'):
|
||||
with open('VERSION', 'r') as v:
|
||||
return v.read()
|
||||
|
|
|
@ -10,7 +10,9 @@ REST_FRAMEWORK = {
|
|||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': os.environ.get('DB_ENGINE', 'django.db.backends.sqlite3'),
|
||||
'TEST': {'NAME': 'chrono-test-%s' % os.environ.get("BRANCH_NAME", "").replace('/', '-')[:63],},
|
||||
'TEST': {
|
||||
'NAME': 'chrono-test-%s' % os.environ.get("BRANCH_NAME", "").replace('/', '-')[:63],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -520,7 +520,9 @@ def test_sync_desks_timeperiod_exceptions_from_ics(mocked_get, capsys):
|
|||
|
||||
|
||||
@override_settings(
|
||||
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||
EXCEPTIONS_SOURCES={
|
||||
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
|
||||
}
|
||||
)
|
||||
def test_timeperiodexception_from_settings():
|
||||
agenda = Agenda(label=u'Test 1 agenda')
|
||||
|
@ -553,7 +555,9 @@ def test_timeperiodexception_from_settings():
|
|||
|
||||
def test_timeperiodexception_from_settings_command():
|
||||
setting = {
|
||||
'EXCEPTIONS_SOURCES': {'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||
'EXCEPTIONS_SOURCES': {
|
||||
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
|
||||
}
|
||||
}
|
||||
agenda = Agenda(label=u'Test 1 agenda')
|
||||
agenda.save()
|
||||
|
@ -1040,7 +1044,9 @@ def test_desk_duplicate_exception_sources():
|
|||
|
||||
|
||||
@override_settings(
|
||||
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||
EXCEPTIONS_SOURCES={
|
||||
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
|
||||
}
|
||||
)
|
||||
def test_desk_duplicate_exception_source_from_settings():
|
||||
agenda = Agenda.objects.create(label='Agenda')
|
||||
|
@ -1247,7 +1253,12 @@ def test_agendas_cancel_events_command_network_error(freezer):
|
|||
@mock.patch('django.contrib.auth.models.Group.role', create=True)
|
||||
@pytest.mark.parametrize(
|
||||
'emails_to_members,emails',
|
||||
[(False, []), (False, ['test@entrouvert.com']), (True, []), (True, ['test@entrouvert.com']),],
|
||||
[
|
||||
(False, []),
|
||||
(False, ['test@entrouvert.com']),
|
||||
(True, []),
|
||||
(True, ['test@entrouvert.com']),
|
||||
],
|
||||
)
|
||||
def test_agenda_notifications_role_email(mocked_role, emails_to_members, emails, mailoutbox):
|
||||
group = Group.objects.create(name='group')
|
||||
|
@ -1305,7 +1316,11 @@ def test_agenda_notifications_email_list(mailoutbox):
|
|||
assert mailoutbox[0].recipients() == recipients
|
||||
assert mailoutbox[0].subject == 'Alert: event "Hop" is full'
|
||||
assert (
|
||||
'view it here: https://example.com/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk,)
|
||||
'view it here: https://example.com/manage/agendas/%s/events/%s/'
|
||||
% (
|
||||
agenda.pk,
|
||||
event.pk,
|
||||
)
|
||||
in mailoutbox[0].body
|
||||
)
|
||||
|
||||
|
|
|
@ -340,7 +340,14 @@ def test_agendas_meetingtype_api(app, some_data, meetings_agenda):
|
|||
|
||||
def test_agendas_desks_api(app, some_data, meetings_agenda):
|
||||
resp = app.get('/api/agenda/%s/desks/' % meetings_agenda.slug)
|
||||
assert resp.json == {'data': [{'text': 'Desk 1', 'id': 'desk-1',}]}
|
||||
assert resp.json == {
|
||||
'data': [
|
||||
{
|
||||
'text': 'Desk 1',
|
||||
'id': 'desk-1',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# wrong kind
|
||||
agenda1 = Agenda.objects.filter(label=u'Foo bar')[0]
|
||||
|
@ -536,10 +543,16 @@ def test_datetimes_api_meetings_agenda_time_change(app):
|
|||
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo', duration=60)
|
||||
for weekday in [0, 5, 6]: # monday, saturday, sunday
|
||||
TimePeriod.objects.create(
|
||||
weekday=weekday, start_time=datetime.time(9, 0), end_time=datetime.time(10, 00), desk=desk,
|
||||
weekday=weekday,
|
||||
start_time=datetime.time(9, 0),
|
||||
end_time=datetime.time(10, 00),
|
||||
desk=desk,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=weekday, start_time=datetime.time(14, 0), end_time=datetime.time(15, 00), desk=desk,
|
||||
weekday=weekday,
|
||||
start_time=datetime.time(14, 0),
|
||||
end_time=datetime.time(15, 00),
|
||||
desk=desk,
|
||||
)
|
||||
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meeting_type.slug)
|
||||
|
@ -607,7 +620,10 @@ def test_datetimes_api_meetings_agenda_with_resources(app):
|
|||
desk2 = Desk.objects.create(agenda=agenda, slug='desk-2')
|
||||
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo-bar')
|
||||
TimePeriod.objects.create(
|
||||
weekday=tomorrow.weekday(), start_time=datetime.time(9, 0), end_time=datetime.time(17, 00), desk=desk,
|
||||
weekday=tomorrow.weekday(),
|
||||
start_time=datetime.time(9, 0),
|
||||
end_time=datetime.time(17, 00),
|
||||
desk=desk,
|
||||
)
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meeting_type.slug)
|
||||
resp = app.get(api_url)
|
||||
|
@ -642,13 +658,23 @@ def test_datetimes_api_meetings_agenda_with_resources(app):
|
|||
# resource 3 is not available from 9h to 10h in this agenda (but desk-1 is free)
|
||||
dt = make_aware(datetime.datetime.combine(tomorrow, datetime.time(9, 0)))
|
||||
event2 = Event.objects.create(
|
||||
agenda=agenda, meeting_type=meeting_type, places=1, full=False, start_datetime=dt, desk=desk2,
|
||||
agenda=agenda,
|
||||
meeting_type=meeting_type,
|
||||
places=1,
|
||||
full=False,
|
||||
start_datetime=dt,
|
||||
desk=desk2,
|
||||
)
|
||||
event2.resources.add(resource3)
|
||||
Booking.objects.create(event=event2)
|
||||
dt = make_aware(datetime.datetime.combine(tomorrow, datetime.time(9, 30)))
|
||||
event3 = Event.objects.create(
|
||||
agenda=agenda, meeting_type=meeting_type, places=1, full=False, start_datetime=dt, desk=desk2,
|
||||
agenda=agenda,
|
||||
meeting_type=meeting_type,
|
||||
places=1,
|
||||
full=False,
|
||||
start_datetime=dt,
|
||||
desk=desk2,
|
||||
)
|
||||
event3.resources.add(resource3)
|
||||
Booking.objects.create(event=event3)
|
||||
|
@ -713,11 +739,17 @@ def test_datetimes_api_meetings_agenda_with_resources(app):
|
|||
]
|
||||
|
||||
# no resources to book
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/?resources=' % (agenda.slug, meeting_type.slug,)
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/?resources=' % (
|
||||
agenda.slug,
|
||||
meeting_type.slug,
|
||||
)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 32
|
||||
assert [s['datetime'] for s in resp.json['data'] if s['disabled'] is True] == []
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meeting_type.slug,)
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (
|
||||
agenda.slug,
|
||||
meeting_type.slug,
|
||||
)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 32
|
||||
assert [s['datetime'] for s in resp.json['data'] if s['disabled'] is True] == []
|
||||
|
@ -727,7 +759,10 @@ def test_datetimes_api_meetings_agenda_with_resources(app):
|
|||
event3.save()
|
||||
event3.resources.clear()
|
||||
desk2.delete()
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meeting_type.slug,)
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/' % (
|
||||
agenda.slug,
|
||||
meeting_type.slug,
|
||||
)
|
||||
resp = app.get(api_url)
|
||||
assert len(resp.json['data']) == 32
|
||||
assert [s['datetime'] for s in resp.json['data'] if s['disabled'] is True] == [
|
||||
|
@ -758,7 +793,10 @@ def test_datetimes_api_meetings_agenda_with_resources(app):
|
|||
]
|
||||
|
||||
# resource is unknown or not valid for this agenda
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/?resources=foobarbaz' % (agenda.slug, meeting_type.slug,)
|
||||
api_url = '/api/agenda/%s/meetings/%s/datetimes/?resources=foobarbaz' % (
|
||||
agenda.slug,
|
||||
meeting_type.slug,
|
||||
)
|
||||
resp = app.get(api_url, status=400)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['reason'] == 'invalid resource: foobarbaz' # legacy
|
||||
|
@ -1280,7 +1318,10 @@ def test_booking_api_meeting_colors(app, user):
|
|||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
||||
resp = app.post(
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id), params={'use_color_for': 'Cooking',},
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id),
|
||||
params={
|
||||
'use_color_for': 'Cooking',
|
||||
},
|
||||
)
|
||||
booking = Booking.objects.get(id=resp.json['booking_id'])
|
||||
assert booking.color.label == 'Cooking'
|
||||
|
@ -1288,14 +1329,20 @@ def test_booking_api_meeting_colors(app, user):
|
|||
|
||||
event_id = datetimes_resp.json['data'][3]['id']
|
||||
resp = app.post_json(
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id), params={'use_color_for': 'Cooking',},
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id),
|
||||
params={
|
||||
'use_color_for': 'Cooking',
|
||||
},
|
||||
)
|
||||
new_booking = Booking.objects.get(id=resp.json['booking_id'])
|
||||
assert new_booking.color.index == 0
|
||||
|
||||
event_id = datetimes_resp.json['data'][4]['id']
|
||||
resp = app.post_json(
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id), params={'use_color_for': 'Swimming',},
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id),
|
||||
params={
|
||||
'use_color_for': 'Swimming',
|
||||
},
|
||||
)
|
||||
new_booking = Booking.objects.get(id=resp.json['booking_id'])
|
||||
assert new_booking.color.label == 'Swimming'
|
||||
|
@ -1304,7 +1351,10 @@ def test_booking_api_meeting_colors(app, user):
|
|||
for i in range((BookingColor.COLOR_COUNT * 2) - 2):
|
||||
event_id = datetimes_resp.json['data'][i]['id']
|
||||
resp = app.post_json(
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id), params={'use_color_for': str(i),},
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id),
|
||||
params={
|
||||
'use_color_for': str(i),
|
||||
},
|
||||
)
|
||||
assert BookingColor.objects.count() == BookingColor.COLOR_COUNT * 2
|
||||
assert BookingColor.objects.distinct('index').order_by().count() == BookingColor.COLOR_COUNT
|
||||
|
@ -1323,7 +1373,10 @@ def test_booking_api_meeting_with_resources(app, user):
|
|||
desk = Desk.objects.create(agenda=agenda, slug='desk')
|
||||
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo-bar')
|
||||
TimePeriod.objects.create(
|
||||
weekday=tomorrow.weekday(), start_time=datetime.time(9, 0), end_time=datetime.time(17, 00), desk=desk,
|
||||
weekday=tomorrow.weekday(),
|
||||
start_time=datetime.time(9, 0),
|
||||
end_time=datetime.time(17, 00),
|
||||
desk=desk,
|
||||
)
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
@ -1872,7 +1925,9 @@ def test_booking_cancellation_post_api(app, some_data, user):
|
|||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
resp = app.post(
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.id, event.id),
|
||||
params={'cancel_callback_url': 'http://example.net/jump/trigger/',},
|
||||
params={
|
||||
'cancel_callback_url': 'http://example.net/jump/trigger/',
|
||||
},
|
||||
)
|
||||
booking_id = resp.json['booking_id']
|
||||
assert Booking.objects.count() == 1
|
||||
|
@ -2345,7 +2400,11 @@ def test_resize_booking_not_found(app, user):
|
|||
def test_resize_booking_not_primary(app, user):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(
|
||||
slug='event', start_datetime=now(), places=5, waiting_list_places=5, agenda=agenda,
|
||||
slug='event',
|
||||
start_datetime=now(),
|
||||
places=5,
|
||||
waiting_list_places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
primary = Booking.objects.create(event=event)
|
||||
secondary = Booking.objects.create(event=event, primary_booking=primary)
|
||||
|
@ -2360,7 +2419,11 @@ def test_resize_booking_not_primary(app, user):
|
|||
def test_resize_booking_cancelled(app, user):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(
|
||||
slug='event', start_datetime=now(), places=5, waiting_list_places=5, agenda=agenda,
|
||||
slug='event',
|
||||
start_datetime=now(),
|
||||
places=5,
|
||||
waiting_list_places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
primary = Booking.objects.create(event=event)
|
||||
primary.cancel()
|
||||
|
@ -2375,10 +2438,18 @@ def test_resize_booking_cancelled(app, user):
|
|||
def test_resize_booking_multi_events(app, user):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event1 = Event.objects.create(
|
||||
slug='event1', start_datetime=now(), places=5, waiting_list_places=5, agenda=agenda,
|
||||
slug='event1',
|
||||
start_datetime=now(),
|
||||
places=5,
|
||||
waiting_list_places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
event2 = Event.objects.create(
|
||||
slug='event2', start_datetime=now(), places=5, waiting_list_places=5, agenda=agenda,
|
||||
slug='event2',
|
||||
start_datetime=now(),
|
||||
places=5,
|
||||
waiting_list_places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
primary = Booking.objects.create(event=event1, in_waiting_list=False)
|
||||
Booking.objects.create(event=event2, in_waiting_list=False, primary_booking=primary)
|
||||
|
@ -2393,7 +2464,11 @@ def test_resize_booking_multi_events(app, user):
|
|||
def test_resize_booking_mixed_waiting_list(app, user):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(
|
||||
slug='event', start_datetime=now(), places=5, waiting_list_places=5, agenda=agenda,
|
||||
slug='event',
|
||||
start_datetime=now(),
|
||||
places=5,
|
||||
waiting_list_places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
primary = Booking.objects.create(event=event, in_waiting_list=False)
|
||||
Booking.objects.create(event=event, primary_booking=primary, in_waiting_list=True)
|
||||
|
@ -2408,7 +2483,11 @@ def test_resize_booking_mixed_waiting_list(app, user):
|
|||
def test_resize_booking(app, user):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(
|
||||
slug='event', start_datetime=now(), places=5, waiting_list_places=5, agenda=agenda,
|
||||
slug='event',
|
||||
start_datetime=now(),
|
||||
places=5,
|
||||
waiting_list_places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
primary = Booking.objects.create(event=event, in_waiting_list=False)
|
||||
# there is other bookings
|
||||
|
@ -2479,7 +2558,11 @@ def test_resize_booking(app, user):
|
|||
def test_resize_booking_in_waiting_list(app, user):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(
|
||||
slug='event', start_datetime=now(), places=5, waiting_list_places=5, agenda=agenda,
|
||||
slug='event',
|
||||
start_datetime=now(),
|
||||
places=5,
|
||||
waiting_list_places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
primary = Booking.objects.create(event=event, in_waiting_list=True)
|
||||
# there is other bookings
|
||||
|
@ -3800,20 +3883,32 @@ def test_virtual_agendas_meetings_datetimes_delays_api(app, mock_now):
|
|||
MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30)
|
||||
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1')
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
weekday=1,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_1,
|
||||
)
|
||||
|
||||
bar_agenda = Agenda.objects.create(label='Bar Meeting', kind='meetings', maximal_booking_delay=7)
|
||||
MeetingType.objects.create(agenda=bar_agenda, label='Meeting Type', duration=30)
|
||||
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1')
|
||||
TimePeriod.objects.create(
|
||||
weekday=2, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1,
|
||||
weekday=2,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=3, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1,
|
||||
weekday=3,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_1,
|
||||
)
|
||||
|
||||
virt_agenda = Agenda.objects.create(label='Virtual Agenda', kind='virtual')
|
||||
|
@ -3851,10 +3946,16 @@ def test_virtual_agendas_meetings_datetimes_exluded_periods(app, mock_now):
|
|||
MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30)
|
||||
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1')
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
weekday=1,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_1,
|
||||
)
|
||||
virt_agenda = Agenda.objects.create(label='Virtual Agenda', kind='virtual')
|
||||
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda)
|
||||
|
@ -3900,10 +4001,16 @@ def test_virtual_agendas_meetings_datetimes_exluded_periods(app, mock_now):
|
|||
# excluded period applies to every desk
|
||||
foo_desk_2 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 2')
|
||||
TimePeriod.objects.create(
|
||||
weekday=3, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_2,
|
||||
weekday=3,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_2,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=4, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_2,
|
||||
weekday=4,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_2,
|
||||
)
|
||||
resp = app.get(api_url)
|
||||
data = resp.json['data']
|
||||
|
@ -3939,16 +4046,28 @@ def test_virtual_agendas_meetings_datetimes_exluded_periods(app, mock_now):
|
|||
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1')
|
||||
bar_desk_2 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 2')
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(14, 0), end_time=datetime.time(16, 0), desk=bar_desk_1,
|
||||
weekday=0,
|
||||
start_time=datetime.time(14, 0),
|
||||
end_time=datetime.time(16, 0),
|
||||
desk=bar_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=1, start_time=datetime.time(14, 0), end_time=datetime.time(16, 0), desk=bar_desk_1,
|
||||
weekday=1,
|
||||
start_time=datetime.time(14, 0),
|
||||
end_time=datetime.time(16, 0),
|
||||
desk=bar_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=2, start_time=datetime.time(14, 0), end_time=datetime.time(16, 0), desk=bar_desk_2,
|
||||
weekday=2,
|
||||
start_time=datetime.time(14, 0),
|
||||
end_time=datetime.time(16, 0),
|
||||
desk=bar_desk_2,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=3, start_time=datetime.time(14, 0), end_time=datetime.time(16, 0), desk=bar_desk_2,
|
||||
weekday=3,
|
||||
start_time=datetime.time(14, 0),
|
||||
end_time=datetime.time(16, 0),
|
||||
desk=bar_desk_2,
|
||||
)
|
||||
resp = app.get(api_url)
|
||||
data = resp.json['data']
|
||||
|
@ -4153,10 +4272,16 @@ def test_virtual_agendas_meetings_booking(app, mock_now, user):
|
|||
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1')
|
||||
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
weekday=1,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_1,
|
||||
)
|
||||
bar_agenda = Agenda.objects.create(
|
||||
label='Bar Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5
|
||||
|
@ -4165,10 +4290,16 @@ def test_virtual_agendas_meetings_booking(app, mock_now, user):
|
|||
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1')
|
||||
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1,
|
||||
weekday=1,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_1,
|
||||
)
|
||||
|
||||
virt_agenda = Agenda.objects.create(
|
||||
|
@ -4227,10 +4358,16 @@ def test_virtual_agendas_meetings_booking_default_policy(app, mock_now, user):
|
|||
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1')
|
||||
foo_desk_2 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 2')
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_2,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_2,
|
||||
)
|
||||
|
||||
bar_agenda = Agenda.objects.create(
|
||||
|
@ -4242,16 +4379,28 @@ def test_virtual_agendas_meetings_booking_default_policy(app, mock_now, user):
|
|||
bar_desk_3 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 3')
|
||||
bar_desk_4 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 3')
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_2,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_2,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_3,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_3,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_4,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_4,
|
||||
)
|
||||
|
||||
virt_agenda = Agenda.objects.create(
|
||||
|
@ -4407,7 +4556,10 @@ def test_unavailabilitycalendar_meetings_datetimes(app, user):
|
|||
desk = Desk.objects.create(agenda=meetings_agenda, label='desk 1')
|
||||
meeting_type = MeetingType.objects.create(agenda=meetings_agenda, label='Meeting Type', duration=30)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(9, 0), end_time=datetime.time(18, 0), desk=desk,
|
||||
weekday=0,
|
||||
start_time=datetime.time(9, 0),
|
||||
end_time=datetime.time(18, 0),
|
||||
desk=desk,
|
||||
)
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
datetimes_url = '/api/agenda/%s/meetings/%s/datetimes/' % (meetings_agenda.slug, meeting_type.slug)
|
||||
|
@ -4451,10 +4603,16 @@ def test_unavailabilitycalendar_on_virtual_datetimes(app, user, mock_now):
|
|||
MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30)
|
||||
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1')
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1,
|
||||
weekday=1,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=foo_desk_1,
|
||||
)
|
||||
virt_agenda = Agenda.objects.create(label='Virtual Agenda', kind='virtual')
|
||||
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda)
|
||||
|
@ -4504,16 +4662,28 @@ def test_unavailabilitycalendar_on_virtual_datetimes(app, user, mock_now):
|
|||
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1')
|
||||
bar_desk_2 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 2')
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1,
|
||||
weekday=1,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_1,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_2,
|
||||
weekday=0,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_2,
|
||||
)
|
||||
TimePeriod.objects.create(
|
||||
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_2,
|
||||
weekday=1,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=bar_desk_2,
|
||||
)
|
||||
|
||||
# bar_agenda has the same time periods than foo_agenda, but no unavailability calendar
|
||||
|
@ -4585,7 +4755,10 @@ def test_meetings_and_virtual_datetimes_date_filter(app):
|
|||
weekday6 = ((localtime(now())).weekday() + 6) % 7
|
||||
for weekday in (weekday1, weekday2, weekday3, weekday4, weekday5, weekday6):
|
||||
TimePeriod.objects.create(
|
||||
weekday=weekday, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=desk_foo,
|
||||
weekday=weekday,
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(12, 0),
|
||||
desk=desk_foo,
|
||||
)
|
||||
|
||||
virtual_agenda = Agenda.objects.create(
|
||||
|
|
|
@ -519,7 +519,11 @@ def test_import_export_notification_settings():
|
|||
def test_import_export_reminder_settings():
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
AgendaReminderSettings.objects.create(
|
||||
agenda=agenda, days=2, send_email=True, send_sms=False, email_extra_info='test',
|
||||
agenda=agenda,
|
||||
days=2,
|
||||
send_email=True,
|
||||
send_sms=False,
|
||||
email_extra_info='test',
|
||||
)
|
||||
output = get_output_of_command('export_site')
|
||||
payload = json.loads(output)
|
||||
|
@ -530,7 +534,11 @@ def test_import_export_reminder_settings():
|
|||
import_site(payload)
|
||||
agenda = Agenda.objects.first()
|
||||
AgendaReminderSettings.objects.get(
|
||||
agenda=agenda, days=2, send_email=True, send_sms=False, email_extra_info='test',
|
||||
agenda=agenda,
|
||||
days=2,
|
||||
send_email=True,
|
||||
send_sms=False,
|
||||
email_extra_info='test',
|
||||
)
|
||||
|
||||
# again - check OneToOneField
|
||||
|
@ -538,7 +546,9 @@ def test_import_export_reminder_settings():
|
|||
|
||||
|
||||
@override_settings(
|
||||
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||
EXCEPTIONS_SOURCES={
|
||||
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
|
||||
}
|
||||
)
|
||||
def test_import_export_time_period_exception_source():
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
|
||||
|
|
|
@ -4,9 +4,9 @@ from chrono.interval import Interval, IntervalSet
|
|||
|
||||
|
||||
def test_interval_set_merge_adjacent():
|
||||
'''
|
||||
Test that adjacent intervals are merged
|
||||
'''
|
||||
"""
|
||||
Test that adjacent intervals are merged
|
||||
"""
|
||||
s = IntervalSet()
|
||||
for i in range(0, 100, 2):
|
||||
s.add(i, i + 1)
|
||||
|
@ -23,9 +23,9 @@ def test_interval_set_from_ordered():
|
|||
|
||||
|
||||
def test_interval_set_overlaps():
|
||||
'''
|
||||
Test overlaps() works.
|
||||
'''
|
||||
"""
|
||||
Test overlaps() works.
|
||||
"""
|
||||
s = IntervalSet()
|
||||
assert not s.overlaps(0, 1)
|
||||
for i in range(0, 100, 2):
|
||||
|
|
|
@ -185,24 +185,32 @@ def test_events_agenda_month_redirect(app, admin_user):
|
|||
|
||||
# only past events, redirect to last event month
|
||||
Event.objects.create(
|
||||
agenda=agenda, places=1, start_datetime=now() - datetime.timedelta(days=60),
|
||||
agenda=agenda,
|
||||
places=1,
|
||||
start_datetime=now() - datetime.timedelta(days=60),
|
||||
)
|
||||
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
|
||||
assert resp.location.endswith('/manage/agendas/%s/2020/5/' % agenda.pk)
|
||||
Event.objects.create(
|
||||
agenda=agenda, places=1, start_datetime=now() - datetime.timedelta(days=30),
|
||||
agenda=agenda,
|
||||
places=1,
|
||||
start_datetime=now() - datetime.timedelta(days=30),
|
||||
)
|
||||
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
|
||||
assert resp.location.endswith('/manage/agendas/%s/2020/6/' % agenda.pk)
|
||||
|
||||
# future events
|
||||
Event.objects.create(
|
||||
agenda=agenda, places=1, start_datetime=now() + datetime.timedelta(days=60),
|
||||
agenda=agenda,
|
||||
places=1,
|
||||
start_datetime=now() + datetime.timedelta(days=60),
|
||||
)
|
||||
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
|
||||
assert resp.location.endswith('/manage/agendas/%s/2020/9/' % agenda.pk)
|
||||
Event.objects.create(
|
||||
agenda=agenda, places=1, start_datetime=now() + datetime.timedelta(days=30),
|
||||
agenda=agenda,
|
||||
places=1,
|
||||
start_datetime=now() + datetime.timedelta(days=30),
|
||||
)
|
||||
resp = app.get('/manage/agendas/%s/month/' % agenda.pk, status=302)
|
||||
assert resp.location.endswith('/manage/agendas/%s/2020/8/' % agenda.pk)
|
||||
|
@ -592,7 +600,11 @@ def test_resource_month_view_dst_change(app, admin_user):
|
|||
make_aware(datetime.datetime(2019, 10, 29, 10, 0)),
|
||||
]:
|
||||
event = Event.objects.create(
|
||||
agenda=agenda, places=1, desk=desk, meeting_type=meetingtype, start_datetime=start_datetime,
|
||||
agenda=agenda,
|
||||
places=1,
|
||||
desk=desk,
|
||||
meeting_type=meetingtype,
|
||||
start_datetime=start_datetime,
|
||||
)
|
||||
event.resources.add(resource)
|
||||
Booking.objects.create(event=event)
|
||||
|
@ -874,7 +886,9 @@ def test_add_agenda(app, admin_user):
|
|||
|
||||
|
||||
@override_settings(
|
||||
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||
EXCEPTIONS_SOURCES={
|
||||
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
|
||||
}
|
||||
)
|
||||
def test_add_agenda_exceptions_from_settings(app, admin_user):
|
||||
app = login(app)
|
||||
|
@ -1726,7 +1740,9 @@ def test_import_events_existing_event(app, admin_user, freezer):
|
|||
app = login(app)
|
||||
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
|
||||
resp.form['events_csv_file'] = Upload(
|
||||
't.csv', b'2016-09-16,18:00,10,5,label,slug\n2016-09-16,18:00,10,5,label,slug\n', 'text/csv',
|
||||
't.csv',
|
||||
b'2016-09-16,18:00,10,5,label,slug\n2016-09-16,18:00,10,5,label,slug\n',
|
||||
'text/csv',
|
||||
)
|
||||
resp.form.submit(status=302)
|
||||
assert agenda.event_set.count() == 1
|
||||
|
@ -1735,7 +1751,9 @@ def test_import_events_existing_event(app, admin_user, freezer):
|
|||
def check_import(date, time, with_alert):
|
||||
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
|
||||
resp.form['events_csv_file'] = Upload(
|
||||
't.csv', b'%s,%s,10,5,label,slug\n' % (date.encode(), time.encode()), 'text/csv',
|
||||
't.csv',
|
||||
b'%s,%s,10,5,label,slug\n' % (date.encode(), time.encode()),
|
||||
'text/csv',
|
||||
)
|
||||
resp = resp.form.submit(status=302).follow()
|
||||
assert agenda.event_set.count() == 1
|
||||
|
@ -1782,7 +1800,9 @@ def test_import_events_existing_event(app, admin_user, freezer):
|
|||
# check there is a message per changed event
|
||||
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
|
||||
resp.form['events_csv_file'] = Upload(
|
||||
't.csv', b'2016-09-16,18:00,10,5,label,slug\n2016-09-16,19:00,10,5,label,other_slug\n', 'text/csv',
|
||||
't.csv',
|
||||
b'2016-09-16,18:00,10,5,label,slug\n2016-09-16,19:00,10,5,label,other_slug\n',
|
||||
'text/csv',
|
||||
)
|
||||
resp.form.submit(status=302)
|
||||
assert agenda.event_set.count() == 2
|
||||
|
@ -2377,7 +2397,9 @@ def test_meetings_agenda_delete_time_period_exception(app, admin_user):
|
|||
|
||||
|
||||
@override_settings(
|
||||
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||
EXCEPTIONS_SOURCES={
|
||||
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
|
||||
}
|
||||
)
|
||||
def test_exception_list(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
|
||||
|
@ -2824,7 +2846,9 @@ END:VCALENDAR"""
|
|||
|
||||
|
||||
@override_settings(
|
||||
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||
EXCEPTIONS_SOURCES={
|
||||
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
|
||||
}
|
||||
)
|
||||
def test_meetings_agenda_time_period_exception_source_from_settings(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
|
||||
|
@ -2877,7 +2901,9 @@ def test_meetings_agenda_time_period_exception_source_try_disable_ics(app, admin
|
|||
|
||||
|
||||
@override_settings(
|
||||
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||
EXCEPTIONS_SOURCES={
|
||||
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},
|
||||
}
|
||||
)
|
||||
def test_meetings_agenda_time_period_exception_source_from_settings(app, admin_user, freezer):
|
||||
freezer.move_to('2020-01-01')
|
||||
|
|
|
@ -274,12 +274,17 @@ def test_clean_time_period_exceptions(transactional_db):
|
|||
|
||||
# extra data that should not be touched
|
||||
other_exception = TimePeriodException.objects.create(
|
||||
desk=desk, start_datetime=start_datetime, end_datetime=end_datetime,
|
||||
desk=desk,
|
||||
start_datetime=start_datetime,
|
||||
end_datetime=end_datetime,
|
||||
)
|
||||
other_source = TimePeriodExceptionSource.objects.create(desk=desk, ics_file='test.ics')
|
||||
# even if wrong desk, this exception is not from settings thus should not get removed
|
||||
exception_from_ics = TimePeriodException.objects.create(
|
||||
desk=new_desk, start_datetime=start_datetime, end_datetime=end_datetime, source=other_source,
|
||||
desk=new_desk,
|
||||
start_datetime=start_datetime,
|
||||
end_datetime=end_datetime,
|
||||
source=other_source,
|
||||
)
|
||||
|
||||
# ensure migration fixes state
|
||||
|
|
Loading…
Reference in New Issue