manager: create event recurrences when end date is specified (#51218)
This commit is contained in:
parent
cfe2742d1f
commit
86018e908e
|
@ -590,7 +590,7 @@ class Agenda(models.Model):
|
|||
else:
|
||||
recurring_events = self.event_set.filter(recurrence_rule__isnull=False)
|
||||
for event in recurring_events:
|
||||
events.extend(event.get_recurrences(min_start, max_start, excluded_datetimes))
|
||||
events.extend(event.get_recurrences(min_start, max_start, excluded_datetimes, slug_separator=':'))
|
||||
|
||||
events.sort(key=lambda x: [getattr(x, field) for field in Event._meta.ordering])
|
||||
return events
|
||||
|
@ -1196,10 +1196,13 @@ class Event(models.Model):
|
|||
)
|
||||
data = clean_import_data(cls, data)
|
||||
if data.get('slug'):
|
||||
cls.objects.update_or_create(slug=data['slug'], defaults=data)
|
||||
return
|
||||
event = cls(**data)
|
||||
event.save()
|
||||
event, _ = cls.objects.update_or_create(slug=data['slug'], defaults=data)
|
||||
else:
|
||||
event = cls(**data)
|
||||
event.save()
|
||||
if event.recurrence_rule and event.recurrence_end_date:
|
||||
event.refresh_from_db()
|
||||
event.create_all_recurrences()
|
||||
|
||||
def export_json(self):
|
||||
recurrence_end_date = (
|
||||
|
@ -1253,8 +1256,6 @@ class Event(models.Model):
|
|||
raise ValueError('Multiple events found for specified datetime.')
|
||||
|
||||
event = events[0]
|
||||
event.slug = event.slug.replace(':', '--')
|
||||
|
||||
with transaction.atomic():
|
||||
try:
|
||||
return Event.objects.get(agenda=self.agenda, slug=event.slug)
|
||||
|
@ -1262,7 +1263,7 @@ class Event(models.Model):
|
|||
event.save()
|
||||
return event
|
||||
|
||||
def get_recurrences(self, min_datetime, max_datetime, excluded_datetimes=None):
|
||||
def get_recurrences(self, min_datetime, max_datetime, excluded_datetimes=None, slug_separator='--'):
|
||||
recurrences = []
|
||||
rrule_set = rruleset()
|
||||
# do not generate recurrences for existing events
|
||||
|
@ -1298,7 +1299,11 @@ class Event(models.Model):
|
|||
event = copy.copy(event_base)
|
||||
# add timezone back
|
||||
aware_start_datetime = make_aware(start_datetime)
|
||||
event.slug = '%s:%s' % (event.slug, aware_start_datetime.strftime('%Y-%m-%d-%H%M'))
|
||||
event.slug = '%s%s%s' % (
|
||||
event.slug,
|
||||
slug_separator,
|
||||
aware_start_datetime.strftime('%Y-%m-%d-%H%M'),
|
||||
)
|
||||
event.start_datetime = aware_start_datetime.astimezone(utc)
|
||||
recurrences.append(event)
|
||||
|
||||
|
@ -1340,6 +1345,11 @@ class Event(models.Model):
|
|||
event__primary_event=self, event__start_datetime__gt=now(), cancellation_datetime__isnull=True
|
||||
).exists()
|
||||
|
||||
def create_all_recurrences(self, excluded_datetimes=None):
|
||||
max_datetime = datetime.datetime.combine(self.recurrence_end_date, datetime.time(0, 0))
|
||||
recurrences = self.get_recurrences(localtime(now()), make_aware(max_datetime), excluded_datetimes)
|
||||
Event.objects.bulk_create(recurrences)
|
||||
|
||||
|
||||
class BookingColor(models.Model):
|
||||
COLOR_COUNT = 8
|
||||
|
|
|
@ -27,8 +27,7 @@ from django.db import transaction
|
|||
from django.forms import ValidationError
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.six import StringIO
|
||||
from django.utils.timezone import make_aware
|
||||
from django.utils.timezone import now
|
||||
from django.utils.timezone import now, localtime, make_aware, make_naive
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from chrono.agendas.models import (
|
||||
|
@ -224,7 +223,16 @@ class EventForm(forms.ModelForm):
|
|||
if field not in self.protected_fields
|
||||
}
|
||||
self.instance.recurrences.update(**update_fields)
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
event = super().save(*args, **kwargs)
|
||||
if event.recurrence_end_date:
|
||||
self.instance.recurrences.filter(start_datetime__gt=event.recurrence_end_date).delete()
|
||||
excluded_datetimes = [
|
||||
make_naive(dt)
|
||||
for dt in self.instance.recurrences.values_list('start_datetime', flat=True)
|
||||
]
|
||||
event.create_all_recurrences(excluded_datetimes)
|
||||
return event
|
||||
|
||||
|
||||
class AgendaResourceForm(forms.Form):
|
||||
|
|
|
@ -1867,7 +1867,7 @@ def test_recurring_events(freezer):
|
|||
assert len(recurrences) == 3
|
||||
|
||||
first_event = recurrences[0]
|
||||
assert first_event.slug == event.slug + ':2021-01-06-1300'
|
||||
assert first_event.slug == event.slug + '--2021-01-06-1300'
|
||||
|
||||
event_json = event.export_json()
|
||||
first_event_json = first_event.export_json()
|
||||
|
@ -1877,7 +1877,7 @@ def test_recurring_events(freezer):
|
|||
second_event = recurrences[1]
|
||||
assert second_event.start_datetime == first_event.start_datetime + datetime.timedelta(days=7)
|
||||
assert second_event.start_datetime.weekday() == first_event.start_datetime.weekday()
|
||||
assert second_event.slug == 'event:2021-01-13-1300'
|
||||
assert second_event.slug == 'event--2021-01-13-1300'
|
||||
|
||||
different_fields = ['slug', 'start_datetime']
|
||||
second_event_json = second_event.export_json()
|
||||
|
@ -1901,8 +1901,8 @@ def test_recurring_events_dst(freezer, settings):
|
|||
recurrences = event.get_recurrences(dt, dt + datetime.timedelta(days=8))
|
||||
event_before_dst, event_after_dst = recurrences
|
||||
assert event_before_dst.start_datetime.hour + 1 == event_after_dst.start_datetime.hour
|
||||
assert event_before_dst.slug == 'agenda-event:2020-10-24-1400'
|
||||
assert event_after_dst.slug == 'agenda-event:2020-10-31-1400'
|
||||
assert event_before_dst.slug == 'agenda-event--2020-10-24-1400'
|
||||
assert event_after_dst.slug == 'agenda-event--2020-10-31-1400'
|
||||
|
||||
freezer.move_to('2020-11-24 12:00')
|
||||
new_recurrences = event.get_recurrences(dt, dt + datetime.timedelta(days=8))
|
||||
|
|
|
@ -200,6 +200,7 @@ def test_import_export_recurring_event(app, freezer):
|
|||
start_datetime=now(),
|
||||
repeat='daily',
|
||||
places=10,
|
||||
slug='test',
|
||||
)
|
||||
event.refresh_from_db()
|
||||
event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=3))
|
||||
|
@ -221,6 +222,20 @@ def test_import_export_recurring_event(app, freezer):
|
|||
assert event.repeat == 'daily'
|
||||
assert event.recurrence_rule == {'freq': DAILY}
|
||||
|
||||
# importing event with end recurrence date creates recurrences
|
||||
event.recurrence_end_date = now() + datetime.timedelta(days=7)
|
||||
event.save()
|
||||
output = get_output_of_command('export_site')
|
||||
import_site(data={}, clean=True)
|
||||
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
f.write(force_bytes(output))
|
||||
f.flush()
|
||||
call_command('import_site', f.name)
|
||||
|
||||
event = Event.objects.get(slug='test')
|
||||
assert Event.objects.filter(primary_event=event).count() == 7
|
||||
|
||||
|
||||
def test_import_export_permissions(app):
|
||||
meetings_agenda = Agenda.objects.create(label='Foo Bar', kind='meetings')
|
||||
|
|
|
@ -1522,6 +1522,42 @@ def test_edit_recurring_event(settings, app, admin_user, freezer):
|
|||
assert 'Delete' not in resp.text
|
||||
|
||||
|
||||
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)
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
|
||||
resp.form['recurrence_end_date'] = (localtime() + datetime.timedelta(days=5)).strftime('%Y-%m-%d')
|
||||
resp = resp.form.submit()
|
||||
|
||||
# recurrences are created automatically
|
||||
event = Event.objects.get(recurrence_rule__isnull=False)
|
||||
assert Event.objects.filter(primary_event=event).count() == 5
|
||||
assert Event.objects.filter(primary_event=event, start_datetime=now()).exists()
|
||||
|
||||
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
|
||||
resp.form['start_datetime_1'] = (localtime() + datetime.timedelta(hours=1)).strftime('%H:%M')
|
||||
resp = resp.form.submit()
|
||||
assert Event.objects.filter(primary_event=event).count() == 5
|
||||
assert Event.objects.filter(
|
||||
primary_event=event, start_datetime=now() + datetime.timedelta(hours=1)
|
||||
).exists()
|
||||
# old recurrences were deleted
|
||||
assert not Event.objects.filter(primary_event=event, start_datetime=now()).exists()
|
||||
|
||||
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
|
||||
resp.form['recurrence_end_date'] = (localtime() + datetime.timedelta(days=6)).strftime('%Y-%m-%d')
|
||||
resp = resp.form.submit()
|
||||
assert Event.objects.filter(primary_event=event).count() == 6
|
||||
|
||||
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id))
|
||||
resp.form['recurrence_end_date'] = (localtime() + datetime.timedelta(days=4)).strftime('%Y-%m-%d')
|
||||
resp = resp.form.submit()
|
||||
assert Event.objects.filter(primary_event=event).count() == 4
|
||||
|
||||
|
||||
def test_booked_places(app, admin_user):
|
||||
agenda = Agenda(label=u'Foo bar')
|
||||
agenda.save()
|
||||
|
|
Loading…
Reference in New Issue