manager: add more granular control over event recurrence (#50560)
This commit is contained in:
parent
2f9bf16a57
commit
6372afc4a5
|
@ -24,6 +24,6 @@ class Command(BaseCommand):
|
|||
help = 'Update event recurrences to reflect exceptions'
|
||||
|
||||
def handle(self, **options):
|
||||
agendas = Agenda.objects.filter(kind='events', event__recurrence_rule__isnull=False).distinct()
|
||||
agendas = Agenda.objects.filter(kind='events', event__recurrence_days__isnull=False).distinct()
|
||||
for agenda in agendas:
|
||||
agenda.update_event_recurrences()
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
# Generated by Django 2.2.19 on 2021-04-21 13:56
|
||||
|
||||
import django.contrib.postgres.fields
|
||||
from dateutil.rrule import DAILY, WEEKLY
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def migrate_recurrence_fields(apps, schema_editor):
|
||||
Event = apps.get_model('agendas', 'Event')
|
||||
|
||||
for event in Event.objects.filter(recurrence_rule__isnull=False):
|
||||
if event.recurrence_rule['freq'] == DAILY:
|
||||
event.recurrence_days = list(range(7))
|
||||
elif event.recurrence_rule['freq'] == WEEKLY:
|
||||
event.recurrence_days = event.recurrence_rule['byweekday']
|
||||
event.recurrence_week_interval = event.recurrence_rule.get('interval', 1)
|
||||
event.save()
|
||||
|
||||
|
||||
def reverse_migrate_recurrence_fields(apps, schema_editor):
|
||||
Event = apps.get_model('agendas', 'Event')
|
||||
|
||||
for event in Event.objects.filter(recurrence_days__isnull=False):
|
||||
rrule = {}
|
||||
if event.recurrence_days == list(range(7)):
|
||||
event.repeat = 'daily'
|
||||
rrule['freq'] = DAILY
|
||||
else:
|
||||
rrule['freq'] = WEEKLY
|
||||
rrule['byweekday'] = event.recurrence_days
|
||||
if event.recurrence_days == list(range(5)):
|
||||
event.repeat = 'weekdays'
|
||||
elif event.recurrence_week_interval == 2:
|
||||
event.repeat = '2-weeks'
|
||||
rrule['interval'] = 2
|
||||
else:
|
||||
event.repeat = 'weekly'
|
||||
event.recurrence_rule = rrule
|
||||
event.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agendas', '0091_lease'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='recurrence_days',
|
||||
field=django.contrib.postgres.fields.ArrayField(
|
||||
base_field=models.IntegerField(
|
||||
choices=[(0, 'Mo'), (1, 'Tu'), (2, 'We'), (3, 'Th'), (4, 'Fr'), (5, 'Sa'), (6, 'Su')]
|
||||
),
|
||||
blank=True,
|
||||
null=True,
|
||||
size=None,
|
||||
verbose_name='Recurrence days',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='recurrence_week_interval',
|
||||
field=models.IntegerField(
|
||||
choices=[(1, 'Every week'), (2, 'Every two weeks'), (3, 'Every three weeks')],
|
||||
default=1,
|
||||
verbose_name='Repeat',
|
||||
),
|
||||
),
|
||||
migrations.RunPython(migrate_recurrence_fields, reverse_migrate_recurrence_fields),
|
||||
migrations.RemoveField(
|
||||
model_name='event',
|
||||
name='recurrence_rule',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='event',
|
||||
name='repeat',
|
||||
),
|
||||
]
|
|
@ -616,7 +616,7 @@ class Agenda(models.Model):
|
|||
entries = self.prefetched_events
|
||||
else:
|
||||
# recurring events are never opened
|
||||
entries = self.event_set.filter(recurrence_rule__isnull=True)
|
||||
entries = self.event_set.filter(recurrence_days__isnull=True)
|
||||
# exclude canceled events except for event recurrences
|
||||
entries = entries.filter(Q(cancelled=False) | Q(primary_event__isnull=False))
|
||||
# we never want to allow booking for past events.
|
||||
|
@ -687,7 +687,7 @@ class Agenda(models.Model):
|
|||
else:
|
||||
recurring_events = self.event_set.filter(
|
||||
Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()),
|
||||
recurrence_rule__isnull=False,
|
||||
recurrence_days__isnull=False,
|
||||
)
|
||||
exceptions = self.get_recurrence_exceptions(min_start, max_start)
|
||||
|
||||
|
@ -705,7 +705,7 @@ class Agenda(models.Model):
|
|||
|
||||
@transaction.atomic
|
||||
def update_event_recurrences(self):
|
||||
recurring_events = self.event_set.filter(recurrence_rule__isnull=False)
|
||||
recurring_events = self.event_set.filter(recurrence_days__isnull=False)
|
||||
recurrences = self.event_set.filter(primary_event__isnull=False)
|
||||
|
||||
# remove recurrences
|
||||
|
@ -1178,17 +1178,31 @@ class MeetingType(models.Model):
|
|||
|
||||
|
||||
class Event(models.Model):
|
||||
REPEAT_CHOICES = [
|
||||
('daily', _('Daily')),
|
||||
('weekly', _('Weekly')),
|
||||
('2-weeks', _('Once every two weeks')),
|
||||
('weekdays', _('Every weekdays (Monday to Friday)')),
|
||||
WEEKDAY_CHOICES = [
|
||||
(0, _('Mo')),
|
||||
(1, _('Tu')),
|
||||
(2, _('We')),
|
||||
(3, _('Th')),
|
||||
(4, _('Fr')),
|
||||
(5, _('Sa')),
|
||||
(6, _('Su')),
|
||||
]
|
||||
|
||||
INTERVAL_CHOICES = [
|
||||
(1, 'Every week'),
|
||||
(2, 'Every two weeks'),
|
||||
(3, 'Every three weeks'),
|
||||
]
|
||||
|
||||
agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE)
|
||||
start_datetime = models.DateTimeField(_('Date/time'))
|
||||
repeat = models.CharField(_('Repeat'), max_length=16, blank=True, choices=REPEAT_CHOICES)
|
||||
recurrence_rule = JSONField(_('Recurrence rule'), null=True, blank=True)
|
||||
recurrence_days = ArrayField(
|
||||
models.IntegerField(choices=WEEKDAY_CHOICES),
|
||||
verbose_name=_('Recurrence days'),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
recurrence_week_interval = models.IntegerField(_('Repeat'), choices=INTERVAL_CHOICES, default=1)
|
||||
recurrence_end_date = models.DateField(_('Recurrence end date'), null=True, blank=True)
|
||||
primary_event = models.ForeignKey('self', null=True, on_delete=models.CASCADE, related_name='recurrences')
|
||||
duration = models.PositiveIntegerField(_('Duration (in minutes)'), default=None, null=True, blank=True)
|
||||
|
@ -1243,7 +1257,6 @@ class Event(models.Model):
|
|||
self.check_full()
|
||||
if not self.slug:
|
||||
self.slug = generate_slug(self, seen_slugs=seen_slugs, agenda=self.agenda)
|
||||
self.recurrence_rule = self.get_recurrence_rule()
|
||||
return super(Event, self).save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
|
@ -1267,7 +1280,7 @@ class Event(models.Model):
|
|||
return False
|
||||
if self.agenda.maximal_booking_delay and self.start_datetime > self.agenda.max_booking_datetime:
|
||||
return False
|
||||
if self.recurrence_rule is not None:
|
||||
if self.recurrence_days is not None:
|
||||
# bookable recurrences probably exist
|
||||
return True
|
||||
if self.agenda.minimal_booking_delay and self.start_datetime < self.agenda.min_booking_datetime:
|
||||
|
@ -1402,7 +1415,7 @@ class Event(models.Model):
|
|||
else:
|
||||
event = cls(**data)
|
||||
event.save()
|
||||
if event.recurrence_rule and event.recurrence_end_date:
|
||||
if event.recurrence_days and event.recurrence_end_date:
|
||||
event.refresh_from_db()
|
||||
event.recurrences.filter(start_datetime__gt=event.recurrence_end_date).delete()
|
||||
update_fields = {
|
||||
|
@ -1431,8 +1444,8 @@ class Event(models.Model):
|
|||
return {
|
||||
'start_datetime': make_naive(self.start_datetime).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'publication_date': self.publication_date.strftime('%Y-%m-%d') if self.publication_date else None,
|
||||
'repeat': self.repeat,
|
||||
'recurrence_rule': self.recurrence_rule,
|
||||
'recurrence_days': self.recurrence_days,
|
||||
'recurrence_week_interval': self.recurrence_week_interval,
|
||||
'recurrence_end_date': recurrence_end_date,
|
||||
'places': self.places,
|
||||
'waiting_list_places': self.waiting_list_places,
|
||||
|
@ -1523,11 +1536,6 @@ class Event(models.Model):
|
|||
url=self.url,
|
||||
)
|
||||
|
||||
if self.recurrence_end_date:
|
||||
self.recurrence_rule['until'] = datetime.datetime.combine(
|
||||
self.recurrence_end_date, datetime.time(0, 0)
|
||||
)
|
||||
|
||||
# remove pytz info because dateutil doesn't support DST changes
|
||||
min_datetime = make_naive(min_datetime)
|
||||
max_datetime = make_naive(max_datetime)
|
||||
|
@ -1548,35 +1556,54 @@ class Event(models.Model):
|
|||
return recurrences
|
||||
|
||||
def get_recurrence_display(self):
|
||||
repeat = str(self.get_repeat_display())
|
||||
time = date_format(localtime(self.start_datetime), 'TIME_FORMAT')
|
||||
if self.repeat in ('weekly', '2-weeks'):
|
||||
day = date_format(localtime(self.start_datetime), 'l')
|
||||
return _('%(every_x_days)s on %(day)s at %(time)s') % {
|
||||
'every_x_days': repeat,
|
||||
'day': day,
|
||||
'time': time,
|
||||
|
||||
days_count = len(self.recurrence_days)
|
||||
if days_count == 7:
|
||||
repeat = _('Daily')
|
||||
elif days_count > 1 and (self.recurrence_days[-1] - self.recurrence_days[0]) == days_count - 1:
|
||||
# days are contiguous
|
||||
repeat = _('From %(weekday)s to %(last_weekday)s') % {
|
||||
'weekday': str(WEEKDAYS[self.recurrence_days[0]]),
|
||||
'last_weekday': str(WEEKDAYS[self.recurrence_days[-1]]),
|
||||
}
|
||||
else:
|
||||
return _('%(every_x_days)s at %(time)s') % {'every_x_days': repeat, 'time': time}
|
||||
repeat = _('On %(weekdays)s') % {
|
||||
'weekdays': ', '.join([str(WEEKDAYS[i]) for i in self.recurrence_days])
|
||||
}
|
||||
|
||||
def get_recurrence_rule(self):
|
||||
rrule = {}
|
||||
if self.repeat == 'daily':
|
||||
rrule['freq'] = DAILY
|
||||
elif self.repeat == 'weekly':
|
||||
rrule['freq'] = WEEKLY
|
||||
rrule['byweekday'] = [localtime(self.start_datetime).weekday()]
|
||||
elif self.repeat == '2-weeks':
|
||||
rrule['freq'] = WEEKLY
|
||||
rrule['byweekday'] = [localtime(self.start_datetime).weekday()]
|
||||
rrule['interval'] = 2
|
||||
elif self.repeat == 'weekdays':
|
||||
rrule['freq'] = WEEKLY
|
||||
rrule['byweekday'] = [i for i in range(5)]
|
||||
else:
|
||||
return None
|
||||
return rrule
|
||||
recurrence_display = _('%(On_day_x)s at %(time)s') % {'On_day_x': repeat, 'time': time}
|
||||
|
||||
if self.recurrence_week_interval > 1:
|
||||
if self.recurrence_week_interval == 2:
|
||||
every_x_weeks = _('every two weeks')
|
||||
elif self.recurrence_week_interval == 3:
|
||||
every_x_weeks = _('every three weeks')
|
||||
recurrence_display = _('%(Every_x_days)s, once %(every_x_weeks)s') % {
|
||||
'Every_x_days': recurrence_display,
|
||||
'every_x_weeks': every_x_weeks,
|
||||
}
|
||||
|
||||
if self.recurrence_end_date:
|
||||
end_date = date_format(self.recurrence_end_date, 'DATE_FORMAT')
|
||||
recurrence_display = _('%(Every_x_days)s, until %(date)s') % {
|
||||
'Every_x_days': recurrence_display,
|
||||
'date': end_date,
|
||||
}
|
||||
return recurrence_display
|
||||
|
||||
@property
|
||||
def recurrence_rule(self):
|
||||
recurrence_rule = {
|
||||
'freq': WEEKLY,
|
||||
'byweekday': self.recurrence_days,
|
||||
'interval': self.recurrence_week_interval,
|
||||
}
|
||||
if self.recurrence_end_date:
|
||||
recurrence_rule['until'] = datetime.datetime.combine(
|
||||
self.recurrence_end_date, datetime.time(0, 0)
|
||||
)
|
||||
return recurrence_rule
|
||||
|
||||
def has_recurrences_booked(self, after=None):
|
||||
return Booking.objects.filter(
|
||||
|
|
|
@ -617,7 +617,7 @@ class Agendas(APIView):
|
|||
).order_by()
|
||||
recurring_event_queryset = Event.objects.filter(
|
||||
Q(publication_date__isnull=True) | Q(publication_date__lte=localtime(now()).date()),
|
||||
recurrence_rule__isnull=False,
|
||||
recurrence_days__isnull=False,
|
||||
)
|
||||
exceptions_desk = Desk.objects.filter(slug='_exceptions_holder').prefetch_related(
|
||||
'unavailability_calendars'
|
||||
|
|
|
@ -53,7 +53,7 @@ from chrono.agendas.models import (
|
|||
)
|
||||
|
||||
from . import widgets
|
||||
from .widgets import SplitDateTimeField
|
||||
from .widgets import SplitDateTimeField, WeekdaysWidget
|
||||
|
||||
|
||||
class AbsenceReasonForm(forms.ModelForm):
|
||||
|
@ -160,22 +160,53 @@ class UnavailabilityCalendarEditForm(UnavailabilityCalendarAddForm):
|
|||
|
||||
|
||||
class NewEventForm(forms.ModelForm):
|
||||
frequency = forms.ChoiceField(
|
||||
label=_('Event frequency'),
|
||||
widget=forms.RadioSelect,
|
||||
choices=(
|
||||
('unique', _('Unique')),
|
||||
('recurring', _('Recurring')),
|
||||
),
|
||||
initial='unique',
|
||||
)
|
||||
recurrence_days = forms.TypedMultipleChoiceField(
|
||||
choices=Event.WEEKDAY_CHOICES, coerce=int, required=False, widget=WeekdaysWidget
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = [
|
||||
'label',
|
||||
'start_datetime',
|
||||
'repeat',
|
||||
'frequency',
|
||||
'recurrence_days',
|
||||
'recurrence_week_interval',
|
||||
'recurrence_end_date',
|
||||
'duration',
|
||||
'places',
|
||||
]
|
||||
field_classes = {
|
||||
'start_datetime': SplitDateTimeField,
|
||||
}
|
||||
widgets = {
|
||||
'recurrence_end_date': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
|
||||
}
|
||||
|
||||
def clean_recurrence_days(self):
|
||||
recurrence_days = self.cleaned_data['recurrence_days']
|
||||
if recurrence_days == []:
|
||||
return None
|
||||
return recurrence_days
|
||||
|
||||
|
||||
class EventForm(forms.ModelForm):
|
||||
protected_fields = ('repeat', 'slug', 'start_datetime')
|
||||
class EventForm(NewEventForm):
|
||||
protected_fields = (
|
||||
'slug',
|
||||
'start_datetime',
|
||||
'frequency',
|
||||
'recurrence_days',
|
||||
'recurrence_week_interval',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
|
@ -187,7 +218,9 @@ class EventForm(forms.ModelForm):
|
|||
'label',
|
||||
'slug',
|
||||
'start_datetime',
|
||||
'repeat',
|
||||
'frequency',
|
||||
'recurrence_days',
|
||||
'recurrence_week_interval',
|
||||
'recurrence_end_date',
|
||||
'duration',
|
||||
'publication_date',
|
||||
|
@ -203,14 +236,22 @@ class EventForm(forms.ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.instance.recurrence_rule and self.instance.has_recurrences_booked():
|
||||
self.fields['frequency'].initial = 'recurring' if self.instance.recurrence_days else 'unique'
|
||||
if self.instance.recurrence_days and self.instance.has_recurrences_booked():
|
||||
for field in self.protected_fields:
|
||||
self.fields[field].disabled = True
|
||||
self.fields[field].help_text = _(
|
||||
'This field cannot be modified because some recurrences have bookings attached to them.'
|
||||
)
|
||||
if self.instance.primary_event:
|
||||
for field in ('slug', 'repeat', 'recurrence_end_date', 'publication_date'):
|
||||
for field in (
|
||||
'slug',
|
||||
'recurrence_end_date',
|
||||
'publication_date',
|
||||
'frequency',
|
||||
'recurrence_days',
|
||||
'recurrence_week_interval',
|
||||
):
|
||||
del self.fields[field]
|
||||
|
||||
def clean(self):
|
||||
|
@ -219,18 +260,21 @@ class EventForm(forms.ModelForm):
|
|||
after=self.cleaned_data['recurrence_end_date']
|
||||
):
|
||||
raise ValidationError(_('Bookings exist after this date.'))
|
||||
if self.cleaned_data.get('recurrence_end_date') and not self.cleaned_data.get('repeat'):
|
||||
raise ValidationError(_('Recurrence end date makes no sense without repetition.'))
|
||||
|
||||
if self.cleaned_data.get('frequency') == 'unique':
|
||||
self.cleaned_data['recurrence_days'] = None
|
||||
self.cleaned_data['recurrence_end_date'] = None
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
with transaction.atomic():
|
||||
if any(field for field in self.changed_data if field in self.protected_fields):
|
||||
self.instance.recurrences.all().delete()
|
||||
elif self.instance.recurrence_rule:
|
||||
elif self.instance.recurrence_days:
|
||||
protected_fields = list(self.protected_fields) + ['recurrence_end_date', 'frequency']
|
||||
update_fields = {
|
||||
field: value
|
||||
for field, value in self.cleaned_data.items()
|
||||
if field != 'recurrence_end_date' and field not in self.protected_fields
|
||||
if field not in protected_fields
|
||||
}
|
||||
self.instance.recurrences.update(**update_fields)
|
||||
|
||||
|
|
|
@ -442,3 +442,7 @@ $booking-colors: (
|
|||
background-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
form div.widget[id^=id_recurrence] {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
{% else %}
|
||||
{% if event.label %}{{ event.label }} / {% endif %}
|
||||
{% endif %}
|
||||
{% if not event.repeat %}
|
||||
{% if not event.recurrence_days %}
|
||||
{% if view_mode == 'day_view' %}{{ event.start_datetime|time }}{% else %}{{ event.start_datetime }}{% endif %}
|
||||
{% else %}
|
||||
{{ event.get_recurrence_display }}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "chrono/manager_agenda_view.html" %}
|
||||
{% load i18n %}
|
||||
{% load i18n gadjo %}
|
||||
|
||||
{% block extrascripts %}
|
||||
{{ block.super }}
|
||||
|
@ -29,10 +29,25 @@
|
|||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{% firstof request.POST.next request.GET.next %}">
|
||||
{{ form.as_p }}
|
||||
{{ form|with_template }}
|
||||
<div class="buttons">
|
||||
<button class="submit-button">{% trans "Save" %}</button>
|
||||
<a class="cancel" href="{{ view.get_success_url }}">{% trans 'Cancel' %}</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
recurrence_fields = $('.widget[id^=id_recurrence]');
|
||||
$('input[type=radio][name=frequency]').change(function() {
|
||||
if(!this.checked)
|
||||
return;
|
||||
if(this.value == 'unique') {
|
||||
recurrence_fields.hide();
|
||||
} else {
|
||||
recurrence_fields.show();
|
||||
}
|
||||
}).change();
|
||||
});
|
||||
</script>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{% spaceless %}
|
||||
<span id="{{ widget.attrs.id }}" class="inputs-buttons-group{% if widget.attrs.class %} {{ widget.attrs.class }}{% endif %}">
|
||||
{% for group, options, index in widget.optgroups %}
|
||||
{% for option in options %}
|
||||
{% include "django/forms/widgets/input.html" with widget=option %}
|
||||
<label{% if option.attrs.id %} for="{{ option.attrs.id }}"{% endif %}>{{ option.label }}</label>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</span>
|
||||
{% endspaceless %}
|
|
@ -1054,7 +1054,7 @@ class AgendaDateView(DateMixin, ViewableAgendaMixin):
|
|||
|
||||
def get_queryset(self):
|
||||
if self.agenda.kind == 'events':
|
||||
queryset = self.agenda.event_set.filter(recurrence_rule__isnull=True)
|
||||
queryset = self.agenda.event_set.filter(recurrence_days__isnull=True)
|
||||
else:
|
||||
self.agenda.prefetch_desks_and_exceptions()
|
||||
if self.agenda.kind == 'meetings':
|
||||
|
@ -1583,7 +1583,7 @@ class AgendaSettings(ManagedAgendaMixin, DetailView):
|
|||
if self.agenda.kind == 'events':
|
||||
context['has_absence_reasons'] = AbsenceReasonGroup.objects.exists()
|
||||
context['has_recurring_events'] = self.agenda.event_set.filter(
|
||||
recurrence_rule__isnull=False
|
||||
recurrence_days__isnull=False
|
||||
).exists()
|
||||
desk = Desk.objects.get(agenda=self.agenda, slug='_exceptions_holder')
|
||||
context['exceptions'] = TimePeriodException.objects.filter(
|
||||
|
@ -1865,7 +1865,7 @@ class EventDetailView(ViewableAgendaMixin, DetailView):
|
|||
pk_url_kwarg = 'event_pk'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if self.get_object().recurrence_rule:
|
||||
if self.get_object().recurrence_days:
|
||||
raise Http404('this view makes no sense for recurring events')
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
@ -1900,7 +1900,7 @@ class EventEditView(ManagedAgendaMixin, UpdateView):
|
|||
if (
|
||||
self.request.GET.get('next') == 'settings'
|
||||
or self.request.POST.get('next') == 'settings'
|
||||
or self.object.recurrence_rule
|
||||
or self.object.recurrence_days
|
||||
):
|
||||
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id})
|
||||
return reverse('chrono-manager-event-view', kwargs={'pk': self.agenda.id, 'event_pk': self.object.id})
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
|
||||
from django.forms.fields import SplitDateTimeField
|
||||
from django.forms.widgets import SplitDateTimeWidget, TimeInput
|
||||
from django.forms.widgets import CheckboxSelectMultiple, SplitDateTimeWidget, TimeInput
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
|
||||
|
@ -59,3 +59,14 @@ class TimeWidget(TimeInput):
|
|||
super(TimeWidget, self).__init__(**kwargs)
|
||||
self.attrs['step'] = '300' # 5 minutes
|
||||
self.attrs['pattern'] = '[0-9]{2}:[0-9]{2}'
|
||||
|
||||
|
||||
class WeekdaysWidget(CheckboxSelectMultiple):
|
||||
template_name = 'chrono/widgets/weekdays.html'
|
||||
|
||||
def id_for_label(self, id_, index=None):
|
||||
"""Workaround CheckboxSelectMultiple id_for_label, which would return empty string when
|
||||
index is None, leading to more complicated JS from our side."""
|
||||
if index is None:
|
||||
index = ''
|
||||
return super(CheckboxSelectMultiple, self).id_for_label(id_, index)
|
||||
|
|
|
@ -224,7 +224,7 @@ def test_agendas_api(app):
|
|||
start_datetime=now(),
|
||||
places=10,
|
||||
agenda=event_agenda,
|
||||
repeat='daily',
|
||||
recurrence_days=list(range(7)),
|
||||
)
|
||||
assert len(event_agenda.get_open_events()) == 2
|
||||
resp = app.get('/api/agenda/', params={'with_open_events': '1'})
|
||||
|
@ -232,7 +232,9 @@ def test_agendas_api(app):
|
|||
|
||||
for i in range(10):
|
||||
event_agenda = Agenda.objects.create(label='Foo bar', category=category_a)
|
||||
Event.objects.create(start_datetime=now(), places=10, agenda=event_agenda, repeat='daily')
|
||||
event = Event.objects.create(
|
||||
start_datetime=now(), places=10, agenda=event_agenda, recurrence_days=[now().weekday()]
|
||||
)
|
||||
TimePeriodException.objects.create(
|
||||
desk=event_agenda.desk_set.get(),
|
||||
start_datetime=now(),
|
||||
|
|
|
@ -193,10 +193,11 @@ def test_datetimes_api_exclude_slots(app):
|
|||
event.delete()
|
||||
|
||||
# recurrent event
|
||||
start_datetime = localtime().replace(hour=12, minute=0)
|
||||
event = Event.objects.create(
|
||||
slug='recurrent',
|
||||
start_datetime=localtime().replace(hour=12, minute=0),
|
||||
repeat='weekly',
|
||||
start_datetime=start_datetime,
|
||||
recurrence_days=[start_datetime.weekday()],
|
||||
places=2,
|
||||
agenda=agenda,
|
||||
)
|
||||
|
@ -479,7 +480,12 @@ def test_datetimes_api_meta(app, freezer):
|
|||
# recurring event
|
||||
Event.objects.all().delete()
|
||||
Event.objects.create(
|
||||
slug='abc', label='Test', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda
|
||||
slug='abc',
|
||||
label='Test',
|
||||
start_datetime=localtime(),
|
||||
recurrence_days=[localtime().weekday()],
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
resp = app.get(api_url)
|
||||
assert resp.json['meta']['first_bookable_slot']['text'] == 'Test (May 27, 2017, 1:12 a.m.)'
|
||||
|
@ -491,7 +497,12 @@ def test_recurring_events_api(app, user, freezer):
|
|||
label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30
|
||||
)
|
||||
base_event = Event.objects.create(
|
||||
slug='abc', label='Test', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda
|
||||
slug='abc',
|
||||
label='Test',
|
||||
start_datetime=localtime(),
|
||||
recurrence_days=[localtime().weekday()],
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
|
@ -565,7 +576,11 @@ def test_recurring_events_api_various_times(app, user, mock_now):
|
|||
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30
|
||||
)
|
||||
event = Event.objects.create(
|
||||
slug='abc', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda
|
||||
slug='abc',
|
||||
start_datetime=localtime(),
|
||||
recurrence_days=[localtime().weekday()],
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
event.refresh_from_db()
|
||||
|
||||
|
@ -606,7 +621,13 @@ def test_recurring_events_api_exceptions(app, user, freezer):
|
|||
agenda = Agenda.objects.create(
|
||||
label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30
|
||||
)
|
||||
Event.objects.create(slug='abc', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda)
|
||||
Event.objects.create(
|
||||
slug='abc',
|
||||
start_datetime=localtime(),
|
||||
recurrence_days=[localtime().weekday()],
|
||||
places=5,
|
||||
agenda=agenda,
|
||||
)
|
||||
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug)
|
||||
data = resp.json['data']
|
||||
|
|
|
@ -200,10 +200,11 @@ def test_booking_api_exclude_slots(app, user):
|
|||
event.delete()
|
||||
|
||||
# recurrent event
|
||||
start_datetime = localtime().replace(hour=12, minute=0)
|
||||
event = Event.objects.create(
|
||||
slug='recurrent',
|
||||
start_datetime=localtime().replace(hour=12, minute=0),
|
||||
repeat='weekly',
|
||||
start_datetime=start_datetime,
|
||||
recurrence_days=[start_datetime.weekday()],
|
||||
places=2,
|
||||
agenda=agenda,
|
||||
)
|
||||
|
|
|
@ -2659,12 +2659,13 @@ def test_agenda_events_day_view(app, admin_user):
|
|||
Event.objects.create(
|
||||
label='xyz', start_datetime=localtime().replace(day=11, month=11, year=2020), places=10, agenda=agenda
|
||||
)
|
||||
recurring_start_datetime = localtime().replace(day=4, month=11, year=2020)
|
||||
event = Event.objects.create(
|
||||
label='abc',
|
||||
start_datetime=localtime().replace(day=4, month=11, year=2020),
|
||||
start_datetime=recurring_start_datetime,
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
repeat='weekly',
|
||||
recurrence_days=[recurring_start_datetime.weekday()],
|
||||
)
|
||||
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
|
@ -2687,7 +2688,11 @@ def test_agenda_events_day_view(app, admin_user):
|
|||
# create another event with recurrence, the same day/time
|
||||
start_datetime = localtime().replace(day=4, month=11, year=2020)
|
||||
event = Event.objects.create(
|
||||
label='def', start_datetime=start_datetime, places=10, agenda=agenda, repeat='weekly'
|
||||
label='def',
|
||||
start_datetime=start_datetime,
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
recurrence_days=[start_datetime.weekday()],
|
||||
)
|
||||
resp = app.get('/manage/agendas/%s/2020/11/11/' % agenda.pk)
|
||||
# the event occurence in DB does not hide recurrence of the second recurrent event
|
||||
|
@ -2727,7 +2732,11 @@ def test_agenda_events_month_view(app, admin_user):
|
|||
# add recurring event on every Wednesday
|
||||
start_datetime = localtime().replace(day=4, month=11, year=2020)
|
||||
event = Event.objects.create(
|
||||
label='abc', start_datetime=start_datetime, places=10, agenda=agenda, repeat='weekly'
|
||||
label='abc',
|
||||
start_datetime=start_datetime,
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
recurrence_days=[start_datetime.weekday()],
|
||||
)
|
||||
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
|
@ -2767,7 +2776,11 @@ def test_agenda_events_month_view(app, admin_user):
|
|||
# create another event with recurrence, the same day/time
|
||||
start_datetime = localtime().replace(day=4, month=11, year=2020)
|
||||
event = Event.objects.create(
|
||||
label='def', start_datetime=start_datetime, places=10, agenda=agenda, repeat='weekly'
|
||||
label='def',
|
||||
start_datetime=start_datetime,
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
recurrence_days=[start_datetime.weekday()],
|
||||
)
|
||||
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, 2020, 12))
|
||||
# the event occurence in DB does not hide recurrence of the second recurrent event
|
||||
|
@ -2850,12 +2863,13 @@ def test_agenda_open_events_view(app, admin_user, manager_user):
|
|||
places=42,
|
||||
)
|
||||
# weekly recurring event, first recurrence is in the past but second is in range
|
||||
start_datetime = now() - datetime.timedelta(days=3)
|
||||
event = Event.objects.create(
|
||||
label='event G',
|
||||
start_datetime=now() - datetime.timedelta(days=3),
|
||||
start_datetime=start_datetime,
|
||||
places=10,
|
||||
agenda=agenda,
|
||||
repeat='weekly',
|
||||
recurrence_days=[start_datetime.weekday()],
|
||||
)
|
||||
resp = app.get('/manage/agendas/%s/events/open/' % agenda.pk)
|
||||
assert 'event A' not in resp.text
|
||||
|
@ -4520,7 +4534,7 @@ def test_recurring_events_manage_exceptions(settings, app, admin_user, freezer):
|
|||
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
|
||||
assert not 'Recurrence exceptions' in resp.text
|
||||
|
||||
event.repeat = 'daily'
|
||||
event.recurrence_days = list(range(7))
|
||||
event.save()
|
||||
|
||||
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, 2021, 7))
|
||||
|
@ -4564,7 +4578,7 @@ def test_recurring_events_exceptions_report(settings, app, admin_user, freezer):
|
|||
event = Event.objects.create(
|
||||
start_datetime=now(),
|
||||
places=10,
|
||||
repeat='daily',
|
||||
recurrence_days=list(range(7)),
|
||||
recurrence_end_date=now() + datetime.timedelta(days=30),
|
||||
agenda=agenda,
|
||||
)
|
||||
|
|
|
@ -83,7 +83,7 @@ def test_add_event(app, admin_user):
|
|||
assert (
|
||||
resp.text.count('Enter a valid date')
|
||||
or resp.text.count('Enter a valid time') == 1
|
||||
or resp.text.count('This field is required.') == 1
|
||||
or resp.text.count('This field is required.') >= 1
|
||||
)
|
||||
|
||||
|
||||
|
@ -223,14 +223,15 @@ def test_edit_recurring_event(settings, app, admin_user, freezer):
|
|||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
|
||||
resp.form['repeat'] = 'weekly'
|
||||
resp.form['frequency'] = 'recurring'
|
||||
resp.form['recurrence_days'] = [localtime().weekday()]
|
||||
resp = resp.form.submit()
|
||||
|
||||
# detail page doesn't exist
|
||||
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.id, event.id), status=404)
|
||||
|
||||
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
|
||||
assert 'Weekly on Tuesday at 1:10 p.m.' in resp.text
|
||||
assert 'On Tuesday at 1:10 p.m.' in resp.text
|
||||
# event is bookable regardless of minimal_booking_delay, since it has bookable recurrences
|
||||
assert len(resp.pyquery.find('.bookable')) == 1
|
||||
|
||||
|
@ -250,11 +251,11 @@ def test_edit_recurring_event(settings, app, admin_user, freezer):
|
|||
|
||||
# but some fields should not be updated
|
||||
assert event_recurrence.slug != event.slug
|
||||
assert not event_recurrence.repeat
|
||||
assert not event_recurrence.recurrence_days
|
||||
|
||||
# changing recurrence attribute removes event recurrences
|
||||
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
|
||||
resp.form['repeat'] = ''
|
||||
resp.form['frequency'] = 'unique'
|
||||
resp = resp.form.submit().follow()
|
||||
assert not Event.objects.filter(primary_event=event).exists()
|
||||
|
||||
|
@ -272,7 +273,9 @@ def test_edit_recurring_event(settings, app, admin_user, freezer):
|
|||
event_recurrence = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=7))
|
||||
Booking.objects.create(event=event_recurrence)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
|
||||
assert 'disabled' in resp.form['repeat'].attrs
|
||||
assert 'disabled' in resp.form['frequency'].attrs
|
||||
assert all('disabled' in resp.form.get('recurrence_days', index=i).attrs for i in range(7))
|
||||
assert 'disabled' in resp.form['recurrence_week_interval'].attrs
|
||||
assert 'disabled' in resp.form['slug'].attrs
|
||||
assert 'disabled' in resp.form['start_datetime_0'].attrs
|
||||
assert 'disabled' in resp.form['start_datetime_1'].attrs
|
||||
|
@ -287,13 +290,22 @@ def test_edit_recurring_event(settings, app, admin_user, freezer):
|
|||
assert 'Delete' not in resp.text
|
||||
|
||||
resp = resp.click('Options')
|
||||
assert {'slug', 'repeat', 'recurrence_end_date', 'publication_date'}.isdisjoint(resp.form.fields)
|
||||
assert {
|
||||
'slug',
|
||||
'frequency',
|
||||
'recurrence_days',
|
||||
'recurence_weekly_interval',
|
||||
'recurrence_end_date',
|
||||
'publication_date',
|
||||
}.isdisjoint(resp.form.fields)
|
||||
|
||||
|
||||
def test_edit_recurring_event_with_end_date(settings, app, admin_user, freezer):
|
||||
freezer.move_to('2021-01-12 12:10')
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(start_datetime=now(), places=10, repeat='daily', agenda=agenda)
|
||||
event = Event.objects.create(
|
||||
start_datetime=now(), places=10, recurrence_days=list(range(7)), agenda=agenda
|
||||
)
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
|
||||
|
@ -301,7 +313,7 @@ def test_edit_recurring_event_with_end_date(settings, app, admin_user, freezer):
|
|||
resp = resp.form.submit()
|
||||
|
||||
# recurrences are created automatically
|
||||
event = Event.objects.get(recurrence_rule__isnull=False)
|
||||
event = Event.objects.get(recurrence_days__isnull=False)
|
||||
assert Event.objects.filter(primary_event=event).count() == 5
|
||||
assert Event.objects.filter(primary_event=event, start_datetime=now()).exists()
|
||||
|
||||
|
@ -345,12 +357,6 @@ def test_edit_recurring_event_with_end_date(settings, app, admin_user, freezer):
|
|||
assert Event.objects.filter(primary_event=event).count() == 4
|
||||
assert 'Bookings exist after this date' in resp.text
|
||||
|
||||
Booking.objects.all().delete()
|
||||
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
|
||||
resp.form['repeat'] = ''
|
||||
resp = resp.form.submit()
|
||||
assert 'Recurrence end date makes no sense without repetition.' in resp.text
|
||||
|
||||
|
||||
def test_booked_places(app, admin_user):
|
||||
agenda = Agenda(label=u'Foo bar')
|
||||
|
@ -444,7 +450,9 @@ def test_delete_busy_event(app, admin_user):
|
|||
def test_delete_recurring_event(app, admin_user, freezer):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
start_datetime = now() + datetime.timedelta(days=10)
|
||||
event = Event.objects.create(start_datetime=start_datetime, places=10, agenda=agenda, repeat='weekly')
|
||||
event = Event.objects.create(
|
||||
start_datetime=start_datetime, places=10, agenda=agenda, recurrence_days=[start_datetime.weekday()]
|
||||
)
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
|
||||
|
|
|
@ -1879,7 +1879,7 @@ def test_recurring_events(freezer):
|
|||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=now(),
|
||||
repeat='weekly',
|
||||
recurrence_days=[now().weekday()],
|
||||
label='Event',
|
||||
places=10,
|
||||
waiting_list_places=10,
|
||||
|
@ -1898,7 +1898,7 @@ def test_recurring_events(freezer):
|
|||
|
||||
event_json = event.export_json()
|
||||
first_event_json = first_event.export_json()
|
||||
different_fields = ['slug', 'repeat', 'recurrence_rule']
|
||||
different_fields = ['slug', 'recurrence_days', 'recurrence_week_interval']
|
||||
assert all(first_event_json[k] == event_json[k] for k in event_json if k not in different_fields)
|
||||
|
||||
second_event = recurrences[1]
|
||||
|
@ -1922,7 +1922,9 @@ def test_recurring_events_dst(freezer, settings):
|
|||
freezer.move_to('2020-10-24 12:00')
|
||||
settings.TIME_ZONE = 'Europe/Brussels'
|
||||
agenda = Agenda.objects.create(label='Agenda', kind='events')
|
||||
event = Event.objects.create(agenda=agenda, start_datetime=now(), repeat='weekly', places=5)
|
||||
event = Event.objects.create(
|
||||
agenda=agenda, start_datetime=now(), recurrence_days=[now().weekday()], places=5
|
||||
)
|
||||
event.refresh_from_db()
|
||||
dt = localtime()
|
||||
recurrences = event.get_recurrences(dt, dt + datetime.timedelta(days=8))
|
||||
|
@ -1940,22 +1942,13 @@ def test_recurring_events_dst(freezer, settings):
|
|||
assert event_after_dst.slug == new_event_after_dst.slug
|
||||
|
||||
|
||||
def test_recurring_events_weekday_midnight(freezer):
|
||||
freezer.move_to('2021-01-06 23:30')
|
||||
weekday = localtime().weekday()
|
||||
agenda = Agenda.objects.create(label='Agenda', kind='events')
|
||||
event = Event.objects.create(agenda=agenda, start_datetime=now(), repeat='weekly', places=5)
|
||||
|
||||
assert event.recurrence_rule['byweekday'][0] == weekday
|
||||
|
||||
|
||||
def test_recurring_events_repeat(freezer):
|
||||
def test_recurring_events_repetition(freezer):
|
||||
freezer.move_to('2021-01-06 12:00') # Wednesday
|
||||
agenda = Agenda.objects.create(label='Agenda', kind='events')
|
||||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=now(),
|
||||
repeat='daily',
|
||||
recurrence_days=list(range(7)), # everyday
|
||||
places=5,
|
||||
)
|
||||
event.refresh_from_db()
|
||||
|
@ -1971,7 +1964,7 @@ def test_recurring_events_repeat(freezer):
|
|||
for i in range(len(recurrences) - 1):
|
||||
assert recurrences[i].start_datetime + datetime.timedelta(days=1) == recurrences[i + 1].start_datetime
|
||||
|
||||
event.repeat = 'weekdays'
|
||||
event.recurrence_days = list(range(5)) # from Monday to Friday
|
||||
event.save()
|
||||
recurrences = event.get_recurrences(
|
||||
localtime() + datetime.timedelta(days=1), localtime() + datetime.timedelta(days=7)
|
||||
|
@ -1981,7 +1974,8 @@ def test_recurring_events_repeat(freezer):
|
|||
assert recurrences[1].start_datetime == start_datetime + datetime.timedelta(days=5)
|
||||
assert recurrences[-1].start_datetime == start_datetime + datetime.timedelta(days=7)
|
||||
|
||||
event.repeat = '2-weeks'
|
||||
event.recurrence_days = [localtime(event.start_datetime).weekday()] # from Monday to Friday
|
||||
event.recurrence_week_interval = 2
|
||||
event.save()
|
||||
recurrences = event.get_recurrences(
|
||||
localtime() + datetime.timedelta(days=3), localtime() + datetime.timedelta(days=45)
|
||||
|
@ -1994,6 +1988,14 @@ def test_recurring_events_repeat(freezer):
|
|||
recurrences[i].start_datetime + datetime.timedelta(days=14) == recurrences[i + 1].start_datetime
|
||||
)
|
||||
|
||||
event.recurrence_days = [3] # Tuesday but start_datetime is a Wednesday
|
||||
event.recurrence_week_interval = 1
|
||||
event.save()
|
||||
recurrences = event.get_recurrences(localtime(), localtime() + datetime.timedelta(days=10))
|
||||
assert len(recurrences) == 2
|
||||
# no recurrence exist on Wednesday
|
||||
assert all(localtime(r.start_datetime).weekday() == 3 for r in recurrences)
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2021-01-06')
|
||||
def test_recurring_events_with_end_date():
|
||||
|
@ -2001,7 +2003,7 @@ def test_recurring_events_with_end_date():
|
|||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=now(),
|
||||
repeat='daily',
|
||||
recurrence_days=list(range(7)),
|
||||
places=5,
|
||||
recurrence_end_date=(now() + datetime.timedelta(days=5)).date(),
|
||||
)
|
||||
|
@ -2019,11 +2021,21 @@ def test_recurring_events_with_end_date():
|
|||
def test_recurring_events_sort(freezer):
|
||||
freezer.move_to('2021-01-06 12:00') # Wednesday
|
||||
agenda = Agenda.objects.create(label='Agenda', kind='events')
|
||||
Event.objects.create(agenda=agenda, slug='a', start_datetime=now(), repeat='daily', places=5)
|
||||
Event.objects.create(agenda=agenda, slug='b', start_datetime=now(), repeat='daily', duration=10, places=5)
|
||||
Event.objects.create(agenda=agenda, slug='c', start_datetime=now(), repeat='daily', duration=5, places=5)
|
||||
Event.objects.create(
|
||||
agenda=agenda, slug='d', start_datetime=now() + datetime.timedelta(hours=1), repeat='daily', places=5
|
||||
agenda=agenda, slug='a', start_datetime=now(), recurrence_days=list(range(7)), places=5
|
||||
)
|
||||
Event.objects.create(
|
||||
agenda=agenda, slug='b', start_datetime=now(), recurrence_days=list(range(7)), duration=10, places=5
|
||||
)
|
||||
Event.objects.create(
|
||||
agenda=agenda, slug='c', start_datetime=now(), recurrence_days=list(range(7)), duration=5, places=5
|
||||
)
|
||||
Event.objects.create(
|
||||
agenda=agenda,
|
||||
slug='d',
|
||||
start_datetime=now() + datetime.timedelta(hours=1),
|
||||
recurrence_days=list(range(7)),
|
||||
places=5,
|
||||
)
|
||||
|
||||
events = agenda.get_open_events()[:8]
|
||||
|
@ -2043,7 +2055,7 @@ def test_recurring_events_exceptions(freezer):
|
|||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=now(),
|
||||
repeat='daily',
|
||||
recurrence_days=list(range(7)),
|
||||
places=5,
|
||||
)
|
||||
event.refresh_from_db()
|
||||
|
@ -2116,14 +2128,14 @@ def test_recurring_events_exceptions_update_recurrences(freezer):
|
|||
daily_event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=now(),
|
||||
repeat='daily',
|
||||
recurrence_days=list(range(7)),
|
||||
places=5,
|
||||
recurrence_end_date=datetime.date(year=2021, month=5, day=8),
|
||||
)
|
||||
weekly_event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=now(),
|
||||
repeat='weekly',
|
||||
recurrence_days=[now().weekday()],
|
||||
places=5,
|
||||
recurrence_end_date=datetime.date(year=2021, month=6, day=1),
|
||||
)
|
||||
|
@ -2132,7 +2144,7 @@ def test_recurring_events_exceptions_update_recurrences(freezer):
|
|||
daily_event_no_end_date = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=now() + datetime.timedelta(hours=2),
|
||||
repeat='daily',
|
||||
recurrence_days=list(range(7)),
|
||||
places=5,
|
||||
)
|
||||
daily_event_no_end_date.refresh_from_db()
|
||||
|
@ -2169,3 +2181,38 @@ def test_recurring_events_exceptions_update_recurrences(freezer):
|
|||
assert Booking.objects.count() == 1
|
||||
assert Event.objects.filter(primary_event=daily_event_no_end_date).count() == 1
|
||||
assert agenda.recurrence_exceptions_report.events.get() == event
|
||||
|
||||
|
||||
def test_recurring_events_display(freezer):
|
||||
freezer.move_to('2021-01-06 12:30')
|
||||
agenda = Agenda.objects.create(label='Agenda', kind='events')
|
||||
event = Event.objects.create(
|
||||
agenda=agenda, start_datetime=now(), recurrence_days=list(range(7)), places=5
|
||||
)
|
||||
|
||||
assert event.get_recurrence_display() == 'Daily at 1:30 p.m.'
|
||||
|
||||
event.recurrence_days = [1, 2, 3, 4]
|
||||
event.save()
|
||||
assert event.get_recurrence_display() == 'From Tuesday to Friday at 1:30 p.m.'
|
||||
|
||||
event.recurrence_days = [4, 5, 6]
|
||||
event.save()
|
||||
assert event.get_recurrence_display() == 'From Friday to Sunday at 1:30 p.m.'
|
||||
|
||||
event.recurrence_days = [1, 4, 6]
|
||||
event.save()
|
||||
assert event.get_recurrence_display() == 'On Tuesday, Friday, Sunday at 1:30 p.m.'
|
||||
|
||||
event.recurrence_days = [0]
|
||||
event.recurrence_week_interval = 2
|
||||
event.save()
|
||||
assert event.get_recurrence_display() == 'On Monday at 1:30 p.m., once every two weeks'
|
||||
|
||||
event.recurrence_week_interval = 3
|
||||
event.recurrence_end_date = now() + datetime.timedelta(days=7)
|
||||
event.save()
|
||||
assert (
|
||||
event.get_recurrence_display()
|
||||
== 'On Monday at 1:30 p.m., once every three weeks, until Jan. 13, 2021'
|
||||
)
|
||||
|
|
|
@ -12,7 +12,6 @@ def test_ensure_jsonb_fields():
|
|||
json_fields = (
|
||||
'extra_data',
|
||||
'booking_errors',
|
||||
'recurrence_rule',
|
||||
)
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
|
@ -34,10 +33,6 @@ def test_ensure_jsonb_fields():
|
|||
'''ALTER TABLE agendas_eventcancellationreport
|
||||
ALTER COLUMN booking_errors TYPE text USING booking_errors::text'''
|
||||
)
|
||||
cursor.execute(
|
||||
'''ALTER TABLE agendas_event
|
||||
ALTER COLUMN recurrence_rule TYPE text USING recurrence_rule::text'''
|
||||
)
|
||||
|
||||
call_command('ensure_jsonb')
|
||||
|
||||
|
|
|
@ -214,7 +214,8 @@ def test_import_export_recurring_event(app, freezer):
|
|||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=now(),
|
||||
repeat='daily',
|
||||
recurrence_days=list(range(7)),
|
||||
recurrence_week_interval=2,
|
||||
places=10,
|
||||
slug='test',
|
||||
)
|
||||
|
@ -235,11 +236,12 @@ def test_import_export_recurring_event(app, freezer):
|
|||
assert Event.objects.count() == 1
|
||||
event = Agenda.objects.get(label='Foo Bar').event_set.first()
|
||||
assert event.primary_event is None
|
||||
assert event.repeat == 'daily'
|
||||
assert event.recurrence_rule == {'freq': DAILY}
|
||||
assert event.recurrence_days == list(range(7))
|
||||
assert event.recurrence_week_interval == 2
|
||||
|
||||
# importing event with end recurrence date creates recurrences
|
||||
event.recurrence_end_date = now() + datetime.timedelta(days=7)
|
||||
event.recurrence_week_interval = 1
|
||||
event.save()
|
||||
output = get_output_of_command('export_site')
|
||||
import_site(data={}, clean=True)
|
||||
|
|
Loading…
Reference in New Issue