0001-manager-add-more-granular-control-over-event-recurre.patch
chrono/agendas/migrations/0079_auto_20210421_1556.py | ||
---|---|---|
1 |
# Generated by Django 2.2.19 on 2021-04-21 13:56 |
|
2 | ||
3 |
import django.contrib.postgres.fields |
|
4 |
from django.db import migrations, models |
|
5 |
from dateutil.rrule import DAILY, WEEKLY |
|
6 | ||
7 | ||
8 |
def migrate_recurrence_fields(apps, schema_editor): |
|
9 |
Event = apps.get_model('agendas', 'Event') |
|
10 | ||
11 |
for event in Event.objects.filter(recurrence_rule__isnull=False): |
|
12 |
if event.recurrence_rule['freq'] == DAILY: |
|
13 |
event.recurrence_days = list(range(7)) |
|
14 |
elif event.recurrence_rule['freq'] == WEEKLY: |
|
15 |
event.recurrence_days = event.recurrence_rule['byweekday'] |
|
16 |
event.recurrence_week_interval = event.recurrence_rule.get('interval', 1) |
|
17 |
event.save() |
|
18 | ||
19 | ||
20 |
def reverse_migrate_recurrence_fields(apps, schema_editor): |
|
21 |
Event = apps.get_model('agendas', 'Event') |
|
22 | ||
23 |
for event in Event.objects.filter(recurrence_days__isnull=False): |
|
24 |
rrule = {} |
|
25 |
if event.recurrence_days == list(range(7)): |
|
26 |
event.repeat = 'daily' |
|
27 |
rrule['freq'] = DAILY |
|
28 |
else: |
|
29 |
rrule['freq'] = WEEKLY |
|
30 |
rrule['byweekday'] = event.recurrence_days |
|
31 |
if event.recurrence_days == list(range(5)): |
|
32 |
event.repeat = 'weekdays' |
|
33 |
elif event.recurrence_week_interval == 2: |
|
34 |
event.repeat = '2-weeks' |
|
35 |
rrule['interval'] = 2 |
|
36 |
else: |
|
37 |
event.repeat = 'weekly' |
|
38 |
event.recurrence_rule = rrule |
|
39 |
event.save() |
|
40 | ||
41 | ||
42 |
class Migration(migrations.Migration): |
|
43 | ||
44 |
dependencies = [ |
|
45 |
('agendas', '0078_absence_reasons'), |
|
46 |
] |
|
47 | ||
48 |
operations = [ |
|
49 |
migrations.AddField( |
|
50 |
model_name='event', |
|
51 |
name='recurrence_days', |
|
52 |
field=django.contrib.postgres.fields.ArrayField( |
|
53 |
base_field=models.IntegerField( |
|
54 |
choices=[(0, 'Mo'), (1, 'Tu'), (2, 'We'), (3, 'Th'), (4, 'Fr'), (5, 'Sa'), (6, 'Su')] |
|
55 |
), |
|
56 |
blank=True, |
|
57 |
null=True, |
|
58 |
size=None, |
|
59 |
verbose_name='Recurrence days', |
|
60 |
), |
|
61 |
), |
|
62 |
migrations.AddField( |
|
63 |
model_name='event', |
|
64 |
name='recurrence_week_interval', |
|
65 |
field=models.IntegerField( |
|
66 |
choices=[(1, 'Every week'), (2, 'Every two weeks'), (3, 'Every three weeks')], |
|
67 |
default=1, |
|
68 |
verbose_name='Repeat', |
|
69 |
), |
|
70 |
), |
|
71 |
migrations.RunPython(migrate_recurrence_fields, reverse_migrate_recurrence_fields), |
|
72 |
migrations.RemoveField( |
|
73 |
model_name='event', |
|
74 |
name='recurrence_rule', |
|
75 |
), |
|
76 |
migrations.RemoveField( |
|
77 |
model_name='event', |
|
78 |
name='repeat', |
|
79 |
), |
|
80 |
] |
chrono/agendas/models.py | ||
---|---|---|
26 | 26 | |
27 | 27 |
import requests |
28 | 28 |
import vobject |
29 |
from dateutil.rrule import rrule, rruleset, DAILY, WEEKLY
|
|
29 |
from dateutil.rrule import rrule, rruleset, WEEKLY |
|
30 | 30 | |
31 | 31 |
import django |
32 | 32 |
from django.conf import settings |
... | ... | |
576 | 576 |
entries = self.prefetched_events |
577 | 577 |
else: |
578 | 578 |
# recurring events are never opened |
579 |
entries = self.event_set.filter(recurrence_rule__isnull=True)
|
|
579 |
entries = self.event_set.filter(recurrence_days__isnull=True)
|
|
580 | 580 |
# exclude canceled events except for event recurrences |
581 | 581 |
entries = entries.filter(Q(cancelled=False) | Q(primary_event__isnull=False)) |
582 | 582 |
# we never want to allow booking for past events. |
... | ... | |
644 | 644 |
if prefetched_queryset: |
645 | 645 |
recurring_events = self.prefetched_recurring_events |
646 | 646 |
else: |
647 |
recurring_events = self.event_set.filter(recurrence_rule__isnull=False)
|
|
647 |
recurring_events = self.event_set.filter(recurrence_days__isnull=False)
|
|
648 | 648 |
for event in recurring_events: |
649 | 649 |
events.extend( |
650 | 650 |
event.get_recurrences( |
... | ... | |
1076 | 1076 | |
1077 | 1077 | |
1078 | 1078 |
class Event(models.Model): |
1079 |
REPEAT_CHOICES = [ |
|
1080 |
('daily', _('Daily')), |
|
1081 |
('weekly', _('Weekly')), |
|
1082 |
('2-weeks', _('Once every two weeks')), |
|
1083 |
('weekdays', _('Every weekdays (Monday to Friday)')), |
|
1079 |
WEEKDAY_CHOICES = [ |
|
1080 |
(0, _('Mo')), |
|
1081 |
(1, _('Tu')), |
|
1082 |
(2, _('We')), |
|
1083 |
(3, _('Th')), |
|
1084 |
(4, _('Fr')), |
|
1085 |
(5, _('Sa')), |
|
1086 |
(6, _('Su')), |
|
1087 |
] |
|
1088 | ||
1089 |
INTERVAL_CHOICES = [ |
|
1090 |
(1, 'Every week'), |
|
1091 |
(2, 'Every two weeks'), |
|
1092 |
(3, 'Every three weeks'), |
|
1084 | 1093 |
] |
1085 | 1094 | |
1086 | 1095 |
agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE) |
1087 | 1096 |
start_datetime = models.DateTimeField(_('Date/time')) |
1088 |
repeat = models.CharField(_('Repeat'), max_length=16, blank=True, choices=REPEAT_CHOICES) |
|
1089 |
recurrence_rule = JSONField(_('Recurrence rule'), null=True) |
|
1097 |
recurrence_days = ArrayField( |
|
1098 |
models.IntegerField(choices=WEEKDAY_CHOICES), |
|
1099 |
verbose_name=_('Recurrence days'), |
|
1100 |
blank=True, |
|
1101 |
null=True, |
|
1102 |
) |
|
1103 |
recurrence_week_interval = models.IntegerField(_('Repeat'), choices=INTERVAL_CHOICES, default=1) |
|
1090 | 1104 |
recurrence_end_date = models.DateField(_('Recurrence end date'), null=True, blank=True) |
1091 | 1105 |
primary_event = models.ForeignKey('self', null=True, on_delete=models.CASCADE, related_name='recurrences') |
1092 | 1106 |
duration = models.PositiveIntegerField(_('Duration (in minutes)'), default=None, null=True, blank=True) |
... | ... | |
1141 | 1155 |
self.check_full() |
1142 | 1156 |
if not self.slug: |
1143 | 1157 |
self.slug = generate_slug(self, seen_slugs=seen_slugs, agenda=self.agenda) |
1144 |
self.recurrence_rule = self.get_recurrence_rule() |
|
1145 | 1158 |
return super(Event, self).save(*args, **kwargs) |
1146 | 1159 | |
1147 | 1160 |
@property |
... | ... | |
1165 | 1178 |
return False |
1166 | 1179 |
if self.agenda.maximal_booking_delay and self.start_datetime > self.agenda.max_booking_datetime: |
1167 | 1180 |
return False |
1168 |
if self.recurrence_rule is not None:
|
|
1181 |
if self.recurrence_days is not None:
|
|
1169 | 1182 |
# bookable recurrences probably exist |
1170 | 1183 |
return True |
1171 | 1184 |
if self.agenda.minimal_booking_delay and self.start_datetime < self.agenda.min_booking_datetime: |
... | ... | |
1288 | 1301 |
else: |
1289 | 1302 |
event = cls(**data) |
1290 | 1303 |
event.save() |
1291 |
if event.recurrence_rule and event.recurrence_end_date:
|
|
1304 |
if event.recurrence_days and event.recurrence_end_date:
|
|
1292 | 1305 |
event.refresh_from_db() |
1293 | 1306 |
event.recurrences.filter(start_datetime__gt=event.recurrence_end_date).delete() |
1294 | 1307 |
update_fields = { |
... | ... | |
1317 | 1330 |
return { |
1318 | 1331 |
'start_datetime': make_naive(self.start_datetime).strftime('%Y-%m-%d %H:%M:%S'), |
1319 | 1332 |
'publication_date': self.publication_date.strftime('%Y-%m-%d') if self.publication_date else None, |
1320 |
'repeat': self.repeat,
|
|
1321 |
'recurrence_rule': self.recurrence_rule,
|
|
1333 |
'recurrence_days': self.recurrence_days,
|
|
1334 |
'recurrence_week_interval': self.recurrence_week_interval,
|
|
1322 | 1335 |
'recurrence_end_date': recurrence_end_date, |
1323 | 1336 |
'places': self.places, |
1324 | 1337 |
'waiting_list_places': self.waiting_list_places, |
... | ... | |
1391 | 1404 | |
1392 | 1405 |
if self.publication_date and self.publication_date > min_datetime.date(): |
1393 | 1406 |
min_datetime = make_aware(datetime.datetime.combine(self.publication_date, datetime.time(0, 0))) |
1394 |
if self.recurrence_end_date: |
|
1395 |
self.recurrence_rule['until'] = datetime.datetime.combine( |
|
1396 |
self.recurrence_end_date, datetime.time(0, 0) |
|
1397 |
) |
|
1398 | 1407 | |
1399 | 1408 |
# remove pytz info because dateutil doesn't support DST changes |
1400 | 1409 |
min_datetime = make_naive(min_datetime) |
... | ... | |
1416 | 1425 |
return recurrences |
1417 | 1426 | |
1418 | 1427 |
def get_recurrence_display(self): |
1419 |
repeat = str(self.get_repeat_display()) |
|
1420 | 1428 |
time = date_format(localtime(self.start_datetime), 'TIME_FORMAT') |
1421 |
if self.repeat in ('weekly', '2-weeks'): |
|
1422 |
day = date_format(localtime(self.start_datetime), 'l') |
|
1423 |
return _('%(every_x_days)s on %(day)s at %(time)s') % { |
|
1424 |
'every_x_days': repeat, |
|
1425 |
'day': day, |
|
1426 |
'time': time, |
|
1429 | ||
1430 |
days_count = len(self.recurrence_days) |
|
1431 |
if days_count == 7: |
|
1432 |
repeat = _('Daily') |
|
1433 |
elif days_count > 1 and (self.recurrence_days[-1] - self.recurrence_days[0]) == days_count - 1: |
|
1434 |
# days are contiguous |
|
1435 |
repeat = _('From %(weekday)s to %(last_weekday)s') % { |
|
1436 |
'weekday': str(WEEKDAYS[self.recurrence_days[0]]), |
|
1437 |
'last_weekday': str(WEEKDAYS[self.recurrence_days[-1]]), |
|
1427 | 1438 |
} |
1428 | 1439 |
else: |
1429 |
return _('%(every_x_days)s at %(time)s') % {'every_x_days': repeat, 'time': time} |
|
1430 | ||
1431 |
def get_recurrence_rule(self): |
|
1432 |
rrule = {} |
|
1433 |
if self.repeat == 'daily': |
|
1434 |
rrule['freq'] = DAILY |
|
1435 |
elif self.repeat == 'weekly': |
|
1436 |
rrule['freq'] = WEEKLY |
|
1437 |
rrule['byweekday'] = [localtime(self.start_datetime).weekday()] |
|
1438 |
elif self.repeat == '2-weeks': |
|
1439 |
rrule['freq'] = WEEKLY |
|
1440 |
rrule['byweekday'] = [localtime(self.start_datetime).weekday()] |
|
1441 |
rrule['interval'] = 2 |
|
1442 |
elif self.repeat == 'weekdays': |
|
1443 |
rrule['freq'] = WEEKLY |
|
1444 |
rrule['byweekday'] = [i for i in range(5)] |
|
1445 |
else: |
|
1446 |
return None |
|
1447 |
return rrule |
|
1440 |
repeat = _('On %(weekdays)s') % { |
|
1441 |
'weekdays': ', '.join([str(WEEKDAYS[i]) for i in self.recurrence_days]) |
|
1442 |
} |
|
1443 | ||
1444 |
recurrence_display = _('%(On_day_x)s at %(time)s') % {'On_day_x': repeat, 'time': time} |
|
1445 | ||
1446 |
if self.recurrence_week_interval > 1: |
|
1447 |
if self.recurrence_week_interval == 2: |
|
1448 |
every_x_weeks = _('every two weeks') |
|
1449 |
elif self.recurrence_week_interval == 3: |
|
1450 |
every_x_weeks = _('every three weeks') |
|
1451 |
recurrence_display = _('%(Every_x_days)s, once %(every_x_weeks)s') % { |
|
1452 |
'Every_x_days': recurrence_display, |
|
1453 |
'every_x_weeks': every_x_weeks, |
|
1454 |
} |
|
1455 | ||
1456 |
if self.recurrence_end_date: |
|
1457 |
end_date = date_format(self.recurrence_end_date, 'DATE_FORMAT') |
|
1458 |
recurrence_display = _('%(Every_x_days)s, until %(date)s') % { |
|
1459 |
'Every_x_days': recurrence_display, |
|
1460 |
'date': end_date, |
|
1461 |
} |
|
1462 |
return recurrence_display |
|
1463 | ||
1464 |
@property |
|
1465 |
def recurrence_rule(self): |
|
1466 |
recurrence_rule = { |
|
1467 |
'freq': WEEKLY, |
|
1468 |
'byweekday': self.recurrence_days, |
|
1469 |
'interval': self.recurrence_week_interval, |
|
1470 |
} |
|
1471 |
if self.recurrence_end_date: |
|
1472 |
recurrence_rule['until'] = datetime.datetime.combine( |
|
1473 |
self.recurrence_end_date, datetime.time(0, 0) |
|
1474 |
) |
|
1475 |
return recurrence_rule |
|
1448 | 1476 | |
1449 | 1477 |
def has_recurrences_booked(self, after=None): |
1450 | 1478 |
return Booking.objects.filter( |
chrono/api/views.py | ||
---|---|---|
550 | 550 |
cancelled=False, |
551 | 551 |
start_datetime__gte=localtime(now()), |
552 | 552 |
).order_by() |
553 |
recurring_event_queryset = Event.objects.filter(recurrence_rule__isnull=False)
|
|
553 |
recurring_event_queryset = Event.objects.filter(recurrence_days__isnull=False)
|
|
554 | 554 |
agendas_queryset = agendas_queryset.filter(kind='events').prefetch_related( |
555 | 555 |
Prefetch( |
556 | 556 |
'event_set', |
chrono/manager/forms.py | ||
---|---|---|
50 | 50 |
) |
51 | 51 | |
52 | 52 |
from . import widgets |
53 |
from .widgets import SplitDateTimeField |
|
53 |
from .widgets import SplitDateTimeField, WeekdaysWidget
|
|
54 | 54 | |
55 | 55 | |
56 | 56 |
class AgendaAddForm(forms.ModelForm): |
... | ... | |
141 | 141 | |
142 | 142 | |
143 | 143 |
class NewEventForm(forms.ModelForm): |
144 |
frequency = forms.ChoiceField( |
|
145 |
label=_('Event frequency'), |
|
146 |
widget=forms.RadioSelect, |
|
147 |
choices=( |
|
148 |
('unique', _('Unique')), |
|
149 |
('recurring', _('Recurring')), |
|
150 |
), |
|
151 |
initial='unique', |
|
152 |
) |
|
153 |
recurrence_days = forms.TypedMultipleChoiceField( |
|
154 |
choices=Event.WEEKDAY_CHOICES, coerce=int, required=False, widget=WeekdaysWidget |
|
155 |
) |
|
156 | ||
144 | 157 |
class Meta: |
145 | 158 |
model = Event |
146 | 159 |
fields = [ |
147 | 160 |
'label', |
148 | 161 |
'start_datetime', |
149 |
'repeat', |
|
162 |
'frequency', |
|
163 |
'recurrence_days', |
|
164 |
'recurrence_week_interval', |
|
165 |
'recurrence_end_date', |
|
150 | 166 |
'duration', |
151 | 167 |
'places', |
152 | 168 |
] |
... | ... | |
154 | 170 |
'start_datetime': SplitDateTimeField, |
155 | 171 |
} |
156 | 172 | |
173 |
def clean_recurrence_days(self): |
|
174 |
recurrence_days = self.cleaned_data['recurrence_days'] |
|
175 |
if recurrence_days == []: |
|
176 |
return None |
|
177 |
return recurrence_days |
|
157 | 178 | |
158 |
class EventForm(forms.ModelForm): |
|
159 |
protected_fields = ('repeat', 'slug', 'start_datetime') |
|
179 | ||
180 |
class EventForm(NewEventForm): |
|
181 |
protected_fields = ( |
|
182 |
'slug', |
|
183 |
'start_datetime', |
|
184 |
'frequency', |
|
185 |
'recurrence_days', |
|
186 |
'recurrence_week_interval', |
|
187 |
) |
|
160 | 188 | |
161 | 189 |
class Meta: |
162 | 190 |
model = Event |
... | ... | |
168 | 196 |
'label', |
169 | 197 |
'slug', |
170 | 198 |
'start_datetime', |
171 |
'repeat', |
|
199 |
'frequency', |
|
200 |
'recurrence_days', |
|
201 |
'recurrence_week_interval', |
|
172 | 202 |
'recurrence_end_date', |
173 | 203 |
'duration', |
174 | 204 |
'publication_date', |
... | ... | |
184 | 214 | |
185 | 215 |
def __init__(self, *args, **kwargs): |
186 | 216 |
super().__init__(*args, **kwargs) |
187 |
if self.instance.recurrence_rule and self.instance.has_recurrences_booked(): |
|
217 |
self.fields['frequency'].initial = 'recurring' if self.instance.recurrence_days else 'unique' |
|
218 |
if self.instance.recurrence_days and self.instance.has_recurrences_booked(): |
|
188 | 219 |
for field in self.protected_fields: |
189 | 220 |
self.fields[field].disabled = True |
190 | 221 |
self.fields[field].help_text = _( |
191 | 222 |
'This field cannot be modified because some recurrences have bookings attached to them.' |
192 | 223 |
) |
193 | 224 |
if self.instance.primary_event: |
194 |
for field in ('slug', 'repeat', 'recurrence_end_date', 'publication_date'): |
|
225 |
for field in ( |
|
226 |
'slug', |
|
227 |
'recurrence_end_date', |
|
228 |
'publication_date', |
|
229 |
'frequency', |
|
230 |
'recurrence_days', |
|
231 |
'recurrence_week_interval', |
|
232 |
): |
|
195 | 233 |
del self.fields[field] |
196 | 234 | |
197 | 235 |
def clean(self): |
... | ... | |
200 | 238 |
after=self.cleaned_data['recurrence_end_date'] |
201 | 239 |
): |
202 | 240 |
raise ValidationError(_('Bookings exist after this date.')) |
203 |
if self.cleaned_data.get('recurrence_end_date') and not self.cleaned_data.get('repeat'): |
|
204 |
raise ValidationError(_('Recurrence end date makes no sense without repetition.')) |
|
241 | ||
242 |
if self.cleaned_data.get('frequency') == 'unique': |
|
243 |
self.cleaned_data['recurrence_days'] = None |
|
244 |
self.cleaned_data['recurrence_end_date'] = None |
|
205 | 245 | |
206 | 246 |
def save(self, *args, **kwargs): |
207 | 247 |
with transaction.atomic(): |
208 | 248 |
if any(field for field in self.changed_data if field in self.protected_fields): |
209 | 249 |
self.instance.recurrences.all().delete() |
210 |
elif self.instance.recurrence_rule:
|
|
250 |
elif self.instance.recurrence_days:
|
|
211 | 251 |
update_fields = { |
212 | 252 |
field: value |
213 | 253 |
for field, value in self.cleaned_data.items() |
214 |
if field not in self.protected_fields |
|
254 |
if field != 'frequency' and field not in self.protected_fields
|
|
215 | 255 |
} |
216 | 256 |
self.instance.recurrences.update(**update_fields) |
217 | 257 |
chrono/manager/static/css/style.scss | ||
---|---|---|
411 | 411 |
background-color: $color; |
412 | 412 |
} |
413 | 413 |
} |
414 | ||
415 |
form div.widget[id^=id_recurrence] { |
|
416 |
padding-left: 1em; |
|
417 |
} |
chrono/manager/templates/chrono/manager_agenda_event_fragment.html | ||
---|---|---|
20 | 20 |
{% else %} |
21 | 21 |
{% if event.label %}{{ event.label }} / {% endif %} |
22 | 22 |
{% endif %} |
23 |
{% if not event.repeat %}
|
|
23 |
{% if not event.recurrence_days %}
|
|
24 | 24 |
{% if view_mode == 'day_view' %}{{ event.start_datetime|time }}{% else %}{{ event.start_datetime }}{% endif %} |
25 | 25 |
{% else %} |
26 | 26 |
{{ event.get_recurrence_display }} |
chrono/manager/templates/chrono/manager_event_form.html | ||
---|---|---|
1 | 1 |
{% extends "chrono/manager_agenda_view.html" %} |
2 |
{% load i18n %} |
|
2 |
{% load i18n gadjo %}
|
|
3 | 3 | |
4 | 4 |
{% block extrascripts %} |
5 | 5 |
{{ block.super }} |
... | ... | |
29 | 29 |
<form method="post" enctype="multipart/form-data"> |
30 | 30 |
{% csrf_token %} |
31 | 31 |
<input type="hidden" name="next" value="{% firstof request.POST.next request.GET.next %}"> |
32 |
{{ form.as_p }}
|
|
32 |
{{ form|with_template }}
|
|
33 | 33 |
<div class="buttons"> |
34 | 34 |
<button class="submit-button">{% trans "Save" %}</button> |
35 | 35 |
<a class="cancel" href="{{ view.get_success_url }}">{% trans 'Cancel' %}</a> |
36 | 36 |
</div> |
37 | ||
38 |
<script> |
|
39 |
$(function () { |
|
40 |
recurrence_fields = $('[id^=id_recurrence]'); |
|
41 |
$('input[type=radio][name=frequency]').change(function() { |
|
42 |
if(!this.checked) |
|
43 |
return; |
|
44 |
if(this.value == 'unique') { |
|
45 |
recurrence_fields.hide(); |
|
46 |
} else { |
|
47 |
recurrence_fields.show(); |
|
48 |
} |
|
49 |
}).change(); |
|
50 |
}); |
|
51 |
</script> |
|
37 | 52 |
</form> |
38 | 53 |
{% endblock %} |
chrono/manager/templates/chrono/widgets/weekdays.html | ||
---|---|---|
1 |
{% spaceless %} |
|
2 |
<span id="{{ widget.attrs.id }}" class="inputs-buttons-group{% if widget.attrs.class %} {{ widget.attrs.class }}{% endif %}"> |
|
3 |
{% for group, options, index in widget.optgroups %} |
|
4 |
{% for option in options %} |
|
5 |
<label{% if option.attrs.id %} for="{{ option.attrs.id }}"{% endif %}>{{ option.label }}</label> |
|
6 |
{% include "django/forms/widgets/input.html" with widget=option %} |
|
7 |
{% endfor %} |
|
8 |
{% endfor %} |
|
9 |
</span> |
|
10 |
{% endspaceless %} |
chrono/manager/views.py | ||
---|---|---|
1052 | 1052 | |
1053 | 1053 |
def get_queryset(self): |
1054 | 1054 |
if self.agenda.kind == 'events': |
1055 |
queryset = self.agenda.event_set.filter(recurrence_rule__isnull=True)
|
|
1055 |
queryset = self.agenda.event_set.filter(recurrence_days__isnull=True)
|
|
1056 | 1056 |
else: |
1057 | 1057 |
self.agenda.prefetch_desks_and_exceptions() |
1058 | 1058 |
if self.agenda.kind == 'meetings': |
... | ... | |
1850 | 1850 |
pk_url_kwarg = 'event_pk' |
1851 | 1851 | |
1852 | 1852 |
def dispatch(self, request, *args, **kwargs): |
1853 |
if self.get_object().recurrence_rule:
|
|
1853 |
if self.get_object().recurrence_days:
|
|
1854 | 1854 |
raise Http404('this view makes no sense for recurring events') |
1855 | 1855 |
return super().dispatch(request, *args, **kwargs) |
1856 | 1856 | |
... | ... | |
1885 | 1885 |
if ( |
1886 | 1886 |
self.request.GET.get('next') == 'settings' |
1887 | 1887 |
or self.request.POST.get('next') == 'settings' |
1888 |
or self.object.recurrence_rule
|
|
1888 |
or self.object.recurrence_days
|
|
1889 | 1889 |
): |
1890 | 1890 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) |
1891 | 1891 |
return reverse('chrono-manager-event-view', kwargs={'pk': self.agenda.id, 'event_pk': self.object.id}) |
chrono/manager/widgets.py | ||
---|---|---|
16 | 16 | |
17 | 17 | |
18 | 18 |
from django.forms.fields import SplitDateTimeField |
19 |
from django.forms.widgets import TimeInput, SplitDateTimeWidget |
|
19 |
from django.forms.widgets import TimeInput, SplitDateTimeWidget, CheckboxSelectMultiple
|
|
20 | 20 |
from django.utils.safestring import mark_safe |
21 | 21 | |
22 | 22 | |
... | ... | |
59 | 59 |
super(TimeWidget, self).__init__(**kwargs) |
60 | 60 |
self.attrs['step'] = '300' # 5 minutes |
61 | 61 |
self.attrs['pattern'] = '[0-9]{2}:[0-9]{2}' |
62 | ||
63 | ||
64 |
class WeekdaysWidget(CheckboxSelectMultiple): |
|
65 |
template_name = 'chrono/widgets/weekdays.html' |
|
66 | ||
67 |
def id_for_label(self, id_, index=None): |
|
68 |
"""Workaround CheckboxSelectMultiple id_for_label, which would return empty string when |
|
69 |
index is None, leading to more complicated JS from our side.""" |
|
70 |
if index is None: |
|
71 |
index = '' |
|
72 |
return super(CheckboxSelectMultiple, self).id_for_label(id_, index) |
tests/manager/test_all.py | ||
---|---|---|
2580 | 2580 |
Event.objects.create( |
2581 | 2581 |
label='xyz', start_datetime=localtime().replace(day=11, month=11, year=2020), places=10, agenda=agenda |
2582 | 2582 |
) |
2583 |
recurring_start_datetime = localtime().replace(day=4, month=11, year=2020) |
|
2583 | 2584 |
event = Event.objects.create( |
2584 | 2585 |
label='abc', |
2585 |
start_datetime=localtime().replace(day=4, month=11, year=2020),
|
|
2586 |
start_datetime=recurring_start_datetime,
|
|
2586 | 2587 |
places=10, |
2587 | 2588 |
agenda=agenda, |
2588 |
repeat='weekly',
|
|
2589 |
recurrence_days=[recurring_start_datetime.weekday()],
|
|
2589 | 2590 |
) |
2590 | 2591 | |
2591 | 2592 |
with CaptureQueriesContext(connection) as ctx: |
... | ... | |
2608 | 2609 |
# create another event with recurrence, the same day/time |
2609 | 2610 |
start_datetime = localtime().replace(day=4, month=11, year=2020) |
2610 | 2611 |
event = Event.objects.create( |
2611 |
label='def', start_datetime=start_datetime, places=10, agenda=agenda, repeat='weekly' |
|
2612 |
label='def', |
|
2613 |
start_datetime=start_datetime, |
|
2614 |
places=10, |
|
2615 |
agenda=agenda, |
|
2616 |
recurrence_days=[start_datetime.weekday()], |
|
2612 | 2617 |
) |
2613 | 2618 |
resp = app.get('/manage/agendas/%s/2020/11/11/' % agenda.pk) |
2614 | 2619 |
# the event occurence in DB does not hide recurrence of the second recurrent event |
... | ... | |
2648 | 2653 |
# add recurring event on every Wednesday |
2649 | 2654 |
start_datetime = localtime().replace(day=4, month=11, year=2020) |
2650 | 2655 |
event = Event.objects.create( |
2651 |
label='abc', start_datetime=start_datetime, places=10, agenda=agenda, repeat='weekly' |
|
2656 |
label='abc', |
|
2657 |
start_datetime=start_datetime, |
|
2658 |
places=10, |
|
2659 |
agenda=agenda, |
|
2660 |
recurrence_days=[start_datetime.weekday()], |
|
2652 | 2661 |
) |
2653 | 2662 | |
2654 | 2663 |
with CaptureQueriesContext(connection) as ctx: |
... | ... | |
2677 | 2686 |
# create another event with recurrence, the same day/time |
2678 | 2687 |
start_datetime = localtime().replace(day=4, month=11, year=2020) |
2679 | 2688 |
event = Event.objects.create( |
2680 |
label='def', start_datetime=start_datetime, places=10, agenda=agenda, repeat='weekly' |
|
2689 |
label='def', |
|
2690 |
start_datetime=start_datetime, |
|
2691 |
places=10, |
|
2692 |
agenda=agenda, |
|
2693 |
recurrence_days=[start_datetime.weekday()], |
|
2681 | 2694 |
) |
2682 | 2695 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, 2020, 12)) |
2683 | 2696 |
# the event occurence in DB does not hide recurrence of the second recurrent event |
... | ... | |
2760 | 2773 |
places=42, |
2761 | 2774 |
) |
2762 | 2775 |
# weekly recurring event, first recurrence is in the past but second is in range |
2776 |
start_datetime = now() - datetime.timedelta(days=3) |
|
2763 | 2777 |
event = Event.objects.create( |
2764 | 2778 |
label='event G', |
2765 |
start_datetime=now() - datetime.timedelta(days=3),
|
|
2779 |
start_datetime=start_datetime,
|
|
2766 | 2780 |
places=10, |
2767 | 2781 |
agenda=agenda, |
2768 |
repeat='weekly',
|
|
2782 |
recurrence_days=[start_datetime.weekday()],
|
|
2769 | 2783 |
) |
2770 | 2784 |
resp = app.get('/manage/agendas/%s/events/open/' % agenda.pk) |
2771 | 2785 |
assert 'event A' not in resp.text |
tests/manager/test_event.py | ||
---|---|---|
83 | 83 |
assert ( |
84 | 84 |
resp.text.count('Enter a valid date') |
85 | 85 |
or resp.text.count('Enter a valid time') == 1 |
86 |
or resp.text.count('This field is required.') == 1
|
|
86 |
or resp.text.count('This field is required.') >= 1
|
|
87 | 87 |
) |
88 | 88 | |
89 | 89 | |
... | ... | |
223 | 223 | |
224 | 224 |
app = login(app) |
225 | 225 |
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id)) |
226 |
resp.form['repeat'] = 'weekly' |
|
226 |
resp.form['frequency'] = 'recurring' |
|
227 |
resp.form['recurrence_days'] = [localtime().weekday()] |
|
227 | 228 |
resp = resp.form.submit() |
228 | 229 | |
229 | 230 |
# detail page doesn't exist |
230 | 231 |
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.id, event.id), status=404) |
231 | 232 | |
232 | 233 |
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200) |
233 |
assert 'Weekly on Tuesday at 1:10 p.m.' in resp.text
|
|
234 |
assert 'On Tuesday at 1:10 p.m.' in resp.text
|
|
234 | 235 |
# event is bookable regardless of minimal_booking_delay, since it has bookable recurrences |
235 | 236 |
assert len(resp.pyquery.find('.bookable')) == 1 |
236 | 237 | |
... | ... | |
250 | 251 | |
251 | 252 |
# changing recurrence attribute removes event recurrences |
252 | 253 |
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id)) |
253 |
resp.form['repeat'] = ''
|
|
254 |
resp.form['frequency'] = 'unique'
|
|
254 | 255 |
resp = resp.form.submit().follow() |
255 | 256 |
assert not Event.objects.filter(primary_event=event).exists() |
256 | 257 | |
... | ... | |
268 | 269 |
event_recurrence = event.get_or_create_event_recurrence(event.start_datetime + datetime.timedelta(days=7)) |
269 | 270 |
Booking.objects.create(event=event_recurrence) |
270 | 271 |
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id)) |
271 |
assert 'disabled' in resp.form['repeat'].attrs |
|
272 |
assert 'disabled' in resp.form['frequency'].attrs |
|
273 |
assert all('disabled' in resp.form.get('recurrence_days', index=i).attrs for i in range(7)) |
|
274 |
assert 'disabled' in resp.form['recurrence_week_interval'].attrs |
|
272 | 275 |
assert 'disabled' in resp.form['slug'].attrs |
273 | 276 |
assert 'disabled' in resp.form['start_datetime_0'].attrs |
274 | 277 |
assert 'disabled' in resp.form['start_datetime_1'].attrs |
... | ... | |
283 | 286 |
assert 'Delete' not in resp.text |
284 | 287 | |
285 | 288 |
resp = resp.click('Options') |
286 |
assert {'slug', 'repeat', 'recurrence_end_date', 'publication_date'}.isdisjoint(resp.form.fields) |
|
289 |
assert { |
|
290 |
'slug', |
|
291 |
'frequency', |
|
292 |
'recurrence_days', |
|
293 |
'recurence_weekly_interval', |
|
294 |
'recurrence_end_date', |
|
295 |
'publication_date', |
|
296 |
}.isdisjoint(resp.form.fields) |
|
287 | 297 | |
288 | 298 | |
289 | 299 |
def test_edit_recurring_event_with_end_date(settings, app, admin_user, freezer): |
290 | 300 |
freezer.move_to('2021-01-12 12:10') |
291 | 301 |
agenda = Agenda.objects.create(label='Foo bar', kind='events') |
292 |
event = Event.objects.create(start_datetime=now(), places=10, repeat='daily', agenda=agenda) |
|
302 |
event = Event.objects.create( |
|
303 |
start_datetime=now(), places=10, recurrence_days=list(range(7)), agenda=agenda |
|
304 |
) |
|
293 | 305 | |
294 | 306 |
app = login(app) |
295 | 307 |
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id)) |
... | ... | |
297 | 309 |
resp = resp.form.submit() |
298 | 310 | |
299 | 311 |
# recurrences are created automatically |
300 |
event = Event.objects.get(recurrence_rule__isnull=False)
|
|
312 |
event = Event.objects.get(recurrence_days__isnull=False)
|
|
301 | 313 |
assert Event.objects.filter(primary_event=event).count() == 5 |
302 | 314 |
assert Event.objects.filter(primary_event=event, start_datetime=now()).exists() |
303 | 315 | |
... | ... | |
339 | 351 |
assert Event.objects.filter(primary_event=event).count() == 4 |
340 | 352 |
assert 'Bookings exist after this date' in resp.text |
341 | 353 | |
342 |
Booking.objects.all().delete() |
|
343 |
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id)) |
|
344 |
resp.form['repeat'] = '' |
|
345 |
resp = resp.form.submit() |
|
346 |
assert 'Recurrence end date makes no sense without repetition.' in resp.text |
|
347 | ||
348 | 354 | |
349 | 355 |
def test_booked_places(app, admin_user): |
350 | 356 |
agenda = Agenda(label=u'Foo bar') |
... | ... | |
438 | 444 |
def test_delete_recurring_event(app, admin_user, freezer): |
439 | 445 |
agenda = Agenda.objects.create(label='Foo bar', kind='events') |
440 | 446 |
start_datetime = now() + datetime.timedelta(days=10) |
441 |
event = Event.objects.create(start_datetime=start_datetime, places=10, agenda=agenda, repeat='weekly') |
|
447 |
event = Event.objects.create( |
|
448 |
start_datetime=start_datetime, places=10, agenda=agenda, recurrence_days=[start_datetime.weekday()] |
|
449 |
) |
|
442 | 450 | |
443 | 451 |
app = login(app) |
444 | 452 |
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200) |
tests/test_agendas.py | ||
---|---|---|
1852 | 1852 |
event = Event.objects.create( |
1853 | 1853 |
agenda=agenda, |
1854 | 1854 |
start_datetime=now(), |
1855 |
repeat='weekly',
|
|
1855 |
recurrence_days=[now().weekday()],
|
|
1856 | 1856 |
label='Event', |
1857 | 1857 |
places=10, |
1858 | 1858 |
waiting_list_places=10, |
... | ... | |
1871 | 1871 | |
1872 | 1872 |
event_json = event.export_json() |
1873 | 1873 |
first_event_json = first_event.export_json() |
1874 |
different_fields = ['slug', 'repeat', 'recurrence_rule']
|
|
1874 |
different_fields = ['slug', 'recurrence_days', 'recurrence_week_interval']
|
|
1875 | 1875 |
assert all(first_event_json[k] == event_json[k] for k in event_json if k not in different_fields) |
1876 | 1876 | |
1877 | 1877 |
second_event = recurrences[1] |
... | ... | |
1895 | 1895 |
freezer.move_to('2020-10-24 12:00') |
1896 | 1896 |
settings.TIME_ZONE = 'Europe/Brussels' |
1897 | 1897 |
agenda = Agenda.objects.create(label='Agenda', kind='events') |
1898 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), repeat='weekly', places=5) |
|
1898 |
event = Event.objects.create( |
|
1899 |
agenda=agenda, start_datetime=now(), recurrence_days=[now().weekday()], places=5 |
|
1900 |
) |
|
1899 | 1901 |
event.refresh_from_db() |
1900 | 1902 |
dt = localtime() |
1901 | 1903 |
recurrences = event.get_recurrences(dt, dt + datetime.timedelta(days=8)) |
... | ... | |
1913 | 1915 |
assert event_after_dst.slug == new_event_after_dst.slug |
1914 | 1916 | |
1915 | 1917 | |
1916 |
def test_recurring_events_weekday_midnight(freezer): |
|
1917 |
freezer.move_to('2021-01-06 23:30') |
|
1918 |
weekday = localtime().weekday() |
|
1919 |
agenda = Agenda.objects.create(label='Agenda', kind='events') |
|
1920 |
event = Event.objects.create(agenda=agenda, start_datetime=now(), repeat='weekly', places=5) |
|
1921 | ||
1922 |
assert event.recurrence_rule['byweekday'][0] == weekday |
|
1923 | ||
1924 | ||
1925 |
def test_recurring_events_repeat(freezer): |
|
1918 |
def test_recurring_events_repetition(freezer): |
|
1926 | 1919 |
freezer.move_to('2021-01-06 12:00') # Wednesday |
1927 | 1920 |
agenda = Agenda.objects.create(label='Agenda', kind='events') |
1928 | 1921 |
event = Event.objects.create( |
1929 | 1922 |
agenda=agenda, |
1930 | 1923 |
start_datetime=now(), |
1931 |
repeat='daily',
|
|
1924 |
recurrence_days=list(range(7)), # everyday
|
|
1932 | 1925 |
places=5, |
1933 | 1926 |
) |
1934 | 1927 |
event.refresh_from_db() |
... | ... | |
1944 | 1937 |
for i in range(len(recurrences) - 1): |
1945 | 1938 |
assert recurrences[i].start_datetime + datetime.timedelta(days=1) == recurrences[i + 1].start_datetime |
1946 | 1939 | |
1947 |
event.repeat = 'weekdays'
|
|
1940 |
event.recurrence_days = list(range(5)) # from Monday to Friday
|
|
1948 | 1941 |
event.save() |
1949 | 1942 |
recurrences = event.get_recurrences( |
1950 | 1943 |
localtime() + datetime.timedelta(days=1), localtime() + datetime.timedelta(days=7) |
... | ... | |
1954 | 1947 |
assert recurrences[1].start_datetime == start_datetime + datetime.timedelta(days=5) |
1955 | 1948 |
assert recurrences[-1].start_datetime == start_datetime + datetime.timedelta(days=7) |
1956 | 1949 | |
1957 |
event.repeat = '2-weeks' |
|
1950 |
event.recurrence_days = [localtime(event.start_datetime).weekday()] # from Monday to Friday |
|
1951 |
event.recurrence_week_interval = 2 |
|
1958 | 1952 |
event.save() |
1959 | 1953 |
recurrences = event.get_recurrences( |
1960 | 1954 |
localtime() + datetime.timedelta(days=3), localtime() + datetime.timedelta(days=45) |
... | ... | |
1967 | 1961 |
recurrences[i].start_datetime + datetime.timedelta(days=14) == recurrences[i + 1].start_datetime |
1968 | 1962 |
) |
1969 | 1963 | |
1964 |
event.recurrence_days = [3] # Tuesday but start_datetime is a Wednesday |
|
1965 |
event.recurrence_week_interval = 1 |
|
1966 |
event.save() |
|
1967 |
recurrences = event.get_recurrences(localtime(), localtime() + datetime.timedelta(days=10)) |
|
1968 |
assert len(recurrences) == 2 |
|
1969 |
# no recurrence exist on Wednesday |
|
1970 |
assert all(localtime(r.start_datetime).weekday() == 3 for r in recurrences) |
|
1971 | ||
1970 | 1972 | |
1971 | 1973 |
@pytest.mark.freeze_time('2021-01-06') |
1972 | 1974 |
def test_recurring_events_with_end_date(): |
... | ... | |
1974 | 1976 |
event = Event.objects.create( |
1975 | 1977 |
agenda=agenda, |
1976 | 1978 |
start_datetime=now(), |
1977 |
repeat='daily',
|
|
1979 |
recurrence_days=list(range(7)),
|
|
1978 | 1980 |
places=5, |
1979 | 1981 |
recurrence_end_date=(now() + datetime.timedelta(days=5)).date(), |
1980 | 1982 |
) |
... | ... | |
1992 | 1994 |
def test_recurring_events_sort(freezer): |
1993 | 1995 |
freezer.move_to('2021-01-06 12:00') # Wednesday |
1994 | 1996 |
agenda = Agenda.objects.create(label='Agenda', kind='events') |
1995 |
Event.objects.create(agenda=agenda, slug='a', start_datetime=now(), repeat='daily', places=5) |
|
1996 |
Event.objects.create(agenda=agenda, slug='b', start_datetime=now(), repeat='daily', duration=10, places=5) |
|
1997 |
Event.objects.create(agenda=agenda, slug='c', start_datetime=now(), repeat='daily', duration=5, places=5) |
|
1998 | 1997 |
Event.objects.create( |
1999 |
agenda=agenda, slug='d', start_datetime=now() + datetime.timedelta(hours=1), repeat='daily', places=5 |
|
1998 |
agenda=agenda, slug='a', start_datetime=now(), recurrence_days=list(range(7)), places=5 |
|
1999 |
) |
|
2000 |
Event.objects.create( |
|
2001 |
agenda=agenda, slug='b', start_datetime=now(), recurrence_days=list(range(7)), duration=10, places=5 |
|
2002 |
) |
|
2003 |
Event.objects.create( |
|
2004 |
agenda=agenda, slug='c', start_datetime=now(), recurrence_days=list(range(7)), duration=5, places=5 |
|
2005 |
) |
|
2006 |
Event.objects.create( |
|
2007 |
agenda=agenda, |
|
2008 |
slug='d', |
|
2009 |
start_datetime=now() + datetime.timedelta(hours=1), |
|
2010 |
recurrence_days=list(range(7)), |
|
2011 |
places=5, |
|
2000 | 2012 |
) |
2001 | 2013 | |
2002 | 2014 |
events = agenda.get_open_events()[:8] |
2003 | 2015 |
assert [e.primary_event.slug for e in events] == ['c', 'b', 'a', 'd', 'c', 'b', 'a', 'd'] |
2016 | ||
2017 | ||
2018 |
def test_recurring_events_display(freezer): |
|
2019 |
freezer.move_to('2021-01-06 12:30') |
|
2020 |
agenda = Agenda.objects.create(label='Agenda', kind='events') |
|
2021 |
event = Event.objects.create( |
|
2022 |
agenda=agenda, start_datetime=now(), recurrence_days=list(range(7)), places=5 |
|
2023 |
) |
|
2024 | ||
2025 |
assert event.get_recurrence_display() == 'Daily at 1:30 p.m.' |
|
2026 | ||
2027 |
event.recurrence_days = [1, 2, 3, 4] |
|
2028 |
event.save() |
|
2029 |
assert event.get_recurrence_display() == 'From Tuesday to Friday at 1:30 p.m.' |
|
2030 | ||
2031 |
event.recurrence_days = [4, 5, 6] |
|
2032 |
event.save() |
|
2033 |
assert event.get_recurrence_display() == 'From Friday to Sunday at 1:30 p.m.' |
|
2034 | ||
2035 |
event.recurrence_days = [1, 4, 6] |
|
2036 |
event.save() |
|
2037 |
assert event.get_recurrence_display() == 'On Tuesday, Friday, Sunday at 1:30 p.m.' |
|
2038 | ||
2039 |
event.recurrence_days = [0] |
|
2040 |
event.recurrence_week_interval = 2 |
|
2041 |
event.save() |
|
2042 |
assert event.get_recurrence_display() == 'On Monday at 1:30 p.m., once every two weeks' |
|
2043 | ||
2044 |
event.recurrence_week_interval = 3 |
|
2045 |
event.recurrence_end_date = now() + datetime.timedelta(days=7) |
|
2046 |
event.save() |
|
2047 |
assert ( |
|
2048 |
event.get_recurrence_display() |
|
2049 |
== 'On Monday at 1:30 p.m., once every three weeks, until Jan. 13, 2021' |
|
2050 |
) |
tests/test_api.py | ||
---|---|---|
299 | 299 |
start_datetime=now(), |
300 | 300 |
places=10, |
301 | 301 |
agenda=event_agenda, |
302 |
repeat='daily',
|
|
302 |
recurrence_days=list(range(7)),
|
|
303 | 303 |
) |
304 | 304 |
assert len(event_agenda.get_open_events()) == 2 |
305 | 305 |
resp = app.get('/api/agenda/', params={'with_open_events': '1'}) |
... | ... | |
547 | 547 |
event.delete() |
548 | 548 | |
549 | 549 |
# recurrent event |
550 |
start_datetime = localtime().replace(hour=12, minute=0) |
|
550 | 551 |
event = Event.objects.create( |
551 | 552 |
slug='recurrent', |
552 |
start_datetime=localtime().replace(hour=12, minute=0),
|
|
553 |
repeat='weekly',
|
|
553 |
start_datetime=start_datetime,
|
|
554 |
recurrence_days=[start_datetime.weekday()],
|
|
554 | 555 |
places=2, |
555 | 556 |
agenda=agenda, |
556 | 557 |
) |
... | ... | |
1257 | 1258 |
event.delete() |
1258 | 1259 | |
1259 | 1260 |
# recurrent event |
1261 |
start_datetime = localtime().replace(hour=12, minute=0) |
|
1260 | 1262 |
event = Event.objects.create( |
1261 | 1263 |
slug='recurrent', |
1262 |
start_datetime=localtime().replace(hour=12, minute=0),
|
|
1263 |
repeat='weekly',
|
|
1264 |
start_datetime=start_datetime,
|
|
1265 |
recurrence_days=[start_datetime.weekday()],
|
|
1264 | 1266 |
places=2, |
1265 | 1267 |
agenda=agenda, |
1266 | 1268 |
) |
... | ... | |
6080 | 6082 |
label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30 |
6081 | 6083 |
) |
6082 | 6084 |
base_event = Event.objects.create( |
6083 |
slug='abc', label='Test', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda |
|
6085 |
slug='abc', |
|
6086 |
label='Test', |
|
6087 |
start_datetime=localtime(), |
|
6088 |
recurrence_days=[localtime().weekday()], |
|
6089 |
places=5, |
|
6090 |
agenda=agenda, |
|
6084 | 6091 |
) |
6085 | 6092 | |
6086 | 6093 |
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug) |
... | ... | |
6148 | 6155 |
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=30 |
6149 | 6156 |
) |
6150 | 6157 |
event = Event.objects.create( |
6151 |
slug='abc', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda |
|
6158 |
slug='abc', |
|
6159 |
start_datetime=localtime(), |
|
6160 |
recurrence_days=[localtime().weekday()], |
|
6161 |
places=5, |
|
6162 |
agenda=agenda, |
|
6152 | 6163 |
) |
6153 | 6164 |
event.refresh_from_db() |
6154 | 6165 |
tests/test_import_export.py | ||
---|---|---|
199 | 199 |
event = Event.objects.create( |
200 | 200 |
agenda=agenda, |
201 | 201 |
start_datetime=now(), |
202 |
repeat='daily', |
|
202 |
recurrence_days=list(range(7)), |
|
203 |
recurrence_week_interval=2, |
|
203 | 204 |
places=10, |
204 | 205 |
slug='test', |
205 | 206 |
) |
... | ... | |
220 | 221 |
assert Event.objects.count() == 1 |
221 | 222 |
event = Agenda.objects.get(label='Foo Bar').event_set.first() |
222 | 223 |
assert event.primary_event is None |
223 |
assert event.repeat == 'daily'
|
|
224 |
assert event.recurrence_rule == {'freq': DAILY}
|
|
224 |
assert event.recurrence_days == list(range(7))
|
|
225 |
assert event.recurrence_week_interval == 2
|
|
225 | 226 | |
226 | 227 |
# importing event with end recurrence date creates recurrences |
227 | 228 |
event.recurrence_end_date = now() + datetime.timedelta(days=7) |
229 |
event.recurrence_week_interval = 1 |
|
228 | 230 |
event.save() |
229 | 231 |
output = get_output_of_command('export_site') |
230 | 232 |
import_site(data={}, clean=True) |
231 |
- |