apply black 20.8b1 formatting

This commit is contained in:
Frédéric Péters 2020-12-29 10:42:33 +01:00
parent bfd2e97eb5
commit eeda0c3f2e
48 changed files with 644 additions and 263 deletions

View File

@ -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,),
),
]

View File

@ -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,),
),
]

View File

@ -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',

View File

@ -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'),
),
]

View File

@ -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',

View File

@ -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',

View File

@ -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'),
),
]

View File

@ -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',

View File

@ -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'],
},
),
]

View File

@ -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',
),
]

View File

@ -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'],
},
),
]

View File

@ -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),
),
]

View File

@ -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',

View File

@ -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',

View File

@ -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')]),
),
]

View File

@ -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')]),
),
]

View File

@ -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',
),
]

View File

@ -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')},
),
]

View File

@ -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),
),
]

View File

@ -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'),
),
]

View File

@ -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',

View File

@ -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',

View File

@ -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),
),
]

View File

@ -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',

View File

@ -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',

View File

@ -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),
),
]

View File

@ -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),
),
]

View File

@ -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),
),
]

View File

@ -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),
),
]

View File

@ -49,7 +49,9 @@ class Migration(migrations.Migration):
),
),
],
options={'ordering': ['label'],},
options={
'ordering': ['label'],
},
),
migrations.AlterField(
model_name='timeperiodexception',

View File

@ -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')]),
),
]

View File

@ -12,5 +12,8 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RemoveField(model_name='timeperiodexception', name='external',),
migrations.RemoveField(
model_name='timeperiodexception',
name='external',
),
]

View File

@ -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')]),
),
]

View File

@ -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):

View File

@ -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)

View File

@ -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):

View File

@ -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:

View File

@ -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():

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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],
},
}
}

View File

@ -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
)

View File

@ -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(

View File

@ -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')

View File

@ -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):

View File

@ -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')

View File

@ -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