0002-manager-create-event-recurrences-when-end-date-is-sp.patch
chrono/agendas/models.py | ||
---|---|---|
590 | 590 |
else: |
591 | 591 |
recurring_events = self.event_set.filter(recurrence_rule__isnull=False) |
592 | 592 |
for event in recurring_events: |
593 |
events.extend(event.get_recurrences(min_start, max_start, excluded_datetimes)) |
|
593 |
events.extend(event.get_recurrences(min_start, max_start, excluded_datetimes, slug_separator=':'))
|
|
594 | 594 | |
595 | 595 |
events.sort(key=lambda x: [getattr(x, field) for field in Event._meta.ordering]) |
596 | 596 |
return events |
... | ... | |
1196 | 1196 |
) |
1197 | 1197 |
data = clean_import_data(cls, data) |
1198 | 1198 |
if data.get('slug'): |
1199 |
cls.objects.update_or_create(slug=data['slug'], defaults=data) |
|
1200 |
return |
|
1201 |
event = cls(**data) |
|
1202 |
event.save() |
|
1199 |
event, _ = cls.objects.update_or_create(slug=data['slug'], defaults=data) |
|
1200 |
else: |
|
1201 |
event = cls(**data) |
|
1202 |
event.save() |
|
1203 |
if event.recurrence_rule and event.recurrence_end_date: |
|
1204 |
event.refresh_from_db() |
|
1205 |
event.create_all_recurrences() |
|
1203 | 1206 | |
1204 | 1207 |
def export_json(self): |
1205 | 1208 |
recurrence_end_date = ( |
... | ... | |
1253 | 1256 |
raise ValueError('Multiple events found for specified datetime.') |
1254 | 1257 | |
1255 | 1258 |
event = events[0] |
1256 |
event.slug = event.slug.replace(':', '--') |
|
1257 | ||
1258 | 1259 |
with transaction.atomic(): |
1259 | 1260 |
try: |
1260 | 1261 |
return Event.objects.get(agenda=self.agenda, slug=event.slug) |
... | ... | |
1262 | 1263 |
event.save() |
1263 | 1264 |
return event |
1264 | 1265 | |
1265 |
def get_recurrences(self, min_datetime, max_datetime, excluded_datetimes=None): |
|
1266 |
def get_recurrences(self, min_datetime, max_datetime, excluded_datetimes=None, slug_separator='--'):
|
|
1266 | 1267 |
recurrences = [] |
1267 | 1268 |
rrule_set = rruleset() |
1268 | 1269 |
# do not generate recurrences for existing events |
... | ... | |
1298 | 1299 |
event = copy.copy(event_base) |
1299 | 1300 |
# add timezone back |
1300 | 1301 |
aware_start_datetime = make_aware(start_datetime) |
1301 |
event.slug = '%s:%s' % (event.slug, aware_start_datetime.strftime('%Y-%m-%d-%H%M')) |
|
1302 |
event.slug = '%s%s%s' % ( |
|
1303 |
event.slug, |
|
1304 |
slug_separator, |
|
1305 |
aware_start_datetime.strftime('%Y-%m-%d-%H%M'), |
|
1306 |
) |
|
1302 | 1307 |
event.start_datetime = aware_start_datetime.astimezone(utc) |
1303 | 1308 |
recurrences.append(event) |
1304 | 1309 | |
... | ... | |
1340 | 1345 |
event__primary_event=self, event__start_datetime__gt=now(), cancellation_datetime__isnull=True |
1341 | 1346 |
).exists() |
1342 | 1347 | |
1348 |
def create_all_recurrences(self): |
|
1349 |
max_datetime = datetime.datetime.combine(self.recurrence_end_date, datetime.time(0, 0)) |
|
1350 |
recurrences = self.get_recurrences(localtime(now()), make_aware(max_datetime)) |
|
1351 |
Event.objects.bulk_create(recurrences) |
|
1352 | ||
1343 | 1353 | |
1344 | 1354 |
class BookingColor(models.Model): |
1345 | 1355 |
COLOR_COUNT = 8 |
chrono/manager/forms.py | ||
---|---|---|
28 | 28 |
from django.utils.encoding import force_text |
29 | 29 |
from django.utils.six import StringIO |
30 | 30 |
from django.utils.timezone import make_aware |
31 |
from django.utils.timezone import now |
|
31 |
from django.utils.timezone import now, localtime, make_aware
|
|
32 | 32 |
from django.utils.translation import ugettext_lazy as _ |
33 | 33 | |
34 | 34 |
from chrono.agendas.models import ( |
... | ... | |
178 | 178 | |
179 | 179 | |
180 | 180 |
class EventForm(forms.ModelForm): |
181 |
protected_fields = ('repeat', 'slug', 'start_datetime') |
|
181 |
protected_fields = ('repeat', 'slug', 'start_datetime', 'recurrence_end_date')
|
|
182 | 182 | |
183 | 183 |
class Meta: |
184 | 184 |
model = Event |
... | ... | |
224 | 224 |
if field not in self.protected_fields |
225 | 225 |
} |
226 | 226 |
self.instance.recurrences.update(**update_fields) |
227 |
return super().save(*args, **kwargs) |
|
227 | ||
228 |
event = super().save(*args, **kwargs) |
|
229 |
if event.recurrence_end_date: |
|
230 |
event.create_all_recurrences() |
|
231 |
return event |
|
228 | 232 | |
229 | 233 | |
230 | 234 |
class AgendaResourceForm(forms.Form): |
tests/test_agendas.py | ||
---|---|---|
1867 | 1867 |
assert len(recurrences) == 3 |
1868 | 1868 | |
1869 | 1869 |
first_event = recurrences[0] |
1870 |
assert first_event.slug == event.slug + ':2021-01-06-1300'
|
|
1870 |
assert first_event.slug == event.slug + '--2021-01-06-1300'
|
|
1871 | 1871 | |
1872 | 1872 |
event_json = event.export_json() |
1873 | 1873 |
first_event_json = first_event.export_json() |
... | ... | |
1877 | 1877 |
second_event = recurrences[1] |
1878 | 1878 |
assert second_event.start_datetime == first_event.start_datetime + datetime.timedelta(days=7) |
1879 | 1879 |
assert second_event.start_datetime.weekday() == first_event.start_datetime.weekday() |
1880 |
assert second_event.slug == 'event:2021-01-13-1300'
|
|
1880 |
assert second_event.slug == 'event--2021-01-13-1300'
|
|
1881 | 1881 | |
1882 | 1882 |
different_fields = ['slug', 'start_datetime'] |
1883 | 1883 |
second_event_json = second_event.export_json() |
... | ... | |
1901 | 1901 |
recurrences = event.get_recurrences(dt, dt + datetime.timedelta(days=8)) |
1902 | 1902 |
event_before_dst, event_after_dst = recurrences |
1903 | 1903 |
assert event_before_dst.start_datetime.hour + 1 == event_after_dst.start_datetime.hour |
1904 |
assert event_before_dst.slug == 'agenda-event:2020-10-24-1400'
|
|
1905 |
assert event_after_dst.slug == 'agenda-event:2020-10-31-1400'
|
|
1904 |
assert event_before_dst.slug == 'agenda-event--2020-10-24-1400'
|
|
1905 |
assert event_after_dst.slug == 'agenda-event--2020-10-31-1400'
|
|
1906 | 1906 | |
1907 | 1907 |
freezer.move_to('2020-11-24 12:00') |
1908 | 1908 |
new_recurrences = event.get_recurrences(dt, dt + datetime.timedelta(days=8)) |
tests/test_import_export.py | ||
---|---|---|
200 | 200 |
start_datetime=now(), |
201 | 201 |
repeat='daily', |
202 | 202 |
places=10, |
203 |
slug='test', |
|
203 | 204 |
) |
204 | 205 |
event.refresh_from_db() |
205 | 206 |
event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=3)) |
... | ... | |
221 | 222 |
assert event.repeat == 'daily' |
222 | 223 |
assert event.recurrence_rule == {'freq': DAILY} |
223 | 224 | |
225 |
# importing event with end recurrence date creates recurrences |
|
226 |
event.recurrence_end_date = now() + datetime.timedelta(days=7) |
|
227 |
event.save() |
|
228 |
output = get_output_of_command('export_site') |
|
229 |
import_site(data={}, clean=True) |
|
230 | ||
231 |
with tempfile.NamedTemporaryFile() as f: |
|
232 |
f.write(force_bytes(output)) |
|
233 |
f.flush() |
|
234 |
call_command('import_site', f.name) |
|
235 | ||
236 |
event = Event.objects.get(slug='test') |
|
237 |
assert Event.objects.filter(primary_event=event).count() == 7 |
|
238 | ||
224 | 239 | |
225 | 240 |
def test_import_export_permissions(app): |
226 | 241 |
meetings_agenda = Agenda.objects.create(label='Foo Bar', kind='meetings') |
tests/test_manager.py | ||
---|---|---|
1509 | 1509 |
assert 'disabled' in resp.form['slug'].attrs |
1510 | 1510 |
assert 'disabled' in resp.form['start_datetime_0'].attrs |
1511 | 1511 |
assert 'disabled' in resp.form['start_datetime_1'].attrs |
1512 |
assert 'disabled' in resp.form['recurrence_end_date'].attrs |
|
1512 | 1513 | |
1513 | 1514 |
# changing it anyway doesn't work |
1514 | 1515 |
resp.form['slug'] = 'changed' |
... | ... | |
1522 | 1523 |
assert 'Delete' not in resp.text |
1523 | 1524 | |
1524 | 1525 | |
1526 |
def test_edit_recurring_event_with_end_date(settings, app, admin_user, freezer): |
|
1527 |
freezer.move_to('2021-01-12 12:10') |
|
1528 |
agenda = Agenda.objects.create(label='Foo bar', kind='events') |
|
1529 |
event = Event.objects.create(start_datetime=now(), places=10, repeat='daily', agenda=agenda) |
|
1530 | ||
1531 |
app = login(app) |
|
1532 |
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id)) |
|
1533 |
resp.form['recurrence_end_date'] = (localtime() + datetime.timedelta(days=5)).strftime('%Y-%m-%d') |
|
1534 |
resp = resp.form.submit() |
|
1535 | ||
1536 |
# recurrences are created automatically |
|
1537 |
event = Event.objects.get(recurrence_rule__isnull=False) |
|
1538 |
assert Event.objects.filter(primary_event=event).count() == 5 |
|
1539 |
assert Event.objects.filter(primary_event=event, start_datetime=now()).exists() |
|
1540 | ||
1541 |
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id)) |
|
1542 |
resp.form['start_datetime_1'] = (localtime() + datetime.timedelta(hours=1)).strftime('%H:%M') |
|
1543 |
resp = resp.form.submit() |
|
1544 |
assert Event.objects.filter(primary_event=event).count() == 5 |
|
1545 |
assert Event.objects.filter( |
|
1546 |
primary_event=event, start_datetime=now() + datetime.timedelta(hours=1) |
|
1547 |
).exists() |
|
1548 |
# old recurrences were deleted |
|
1549 |
assert not Event.objects.filter(primary_event=event, start_datetime=now()).exists() |
|
1550 | ||
1551 | ||
1525 | 1552 |
def test_booked_places(app, admin_user): |
1526 | 1553 |
agenda = Agenda(label=u'Foo bar') |
1527 | 1554 |
agenda.save() |
1528 |
- |