Projet

Général

Profil

0001-manager-add-new-recurrence-fields-50560.patch

Valentin Deniaud, 12 avril 2021 17:58

Télécharger (10,8 ko)

Voir les différences:

Subject: [PATCH] manager: add new recurrence fields (#50560)

 .../migrations/0079_auto_20210412_1747.py     | 52 +++++++++++++++++++
 chrono/agendas/models.py                      | 29 ++++++++++-
 chrono/manager/forms.py                       | 33 +++++++++---
 .../templates/chrono/manager_event_form.html  |  4 +-
 .../templates/chrono/widgets/weekdays.html    | 10 ++++
 chrono/manager/widgets.py                     |  6 ++-
 6 files changed, 122 insertions(+), 12 deletions(-)
 create mode 100644 chrono/agendas/migrations/0079_auto_20210412_1747.py
 create mode 100644 chrono/manager/templates/chrono/widgets/weekdays.html
chrono/agendas/migrations/0079_auto_20210412_1747.py
1
# Generated by Django 2.2.19 on 2021-04-12 15:47
2

  
3
import django.contrib.postgres.fields
4
from django.db import migrations, models
5

  
6

  
7
class Migration(migrations.Migration):
8

  
9
    dependencies = [
10
        ('agendas', '0078_absence_reasons'),
11
    ]
12

  
13
    operations = [
14
        migrations.AddField(
15
            model_name='event',
16
            name='recurrence_days',
17
            field=django.contrib.postgres.fields.ArrayField(
18
                base_field=models.IntegerField(
19
                    choices=[(0, 'Mo'), (1, 'Tu'), (2, 'We'), (3, 'Th'), (4, 'Fr'), (5, 'Sa'), (6, 'Su')]
20
                ),
21
                blank=True,
22
                default=list,
23
                null=True,
24
                size=None,
25
                verbose_name='Recurrence days',
26
            ),
27
        ),
28
        migrations.AddField(
29
            model_name='event',
30
            name='recurrence_hour',
31
            field=models.TimeField(blank=True, null=True, verbose_name='Hour'),
32
        ),
33
        migrations.AddField(
34
            model_name='event',
35
            name='recurrence_start_date',
36
            field=models.DateField(blank=True, null=True, verbose_name='Recurrence start date'),
37
        ),
38
        migrations.AddField(
39
            model_name='event',
40
            name='recurrence_weekly_interval',
41
            field=models.IntegerField(
42
                choices=[(1, 'Every week'), (2, 'Every two weeks'), (3, 'Every three weeks')],
43
                default=1,
44
                verbose_name='Repeat',
45
            ),
46
        ),
47
        migrations.AlterField(
48
            model_name='event',
49
            name='start_datetime',
50
            field=models.DateTimeField(blank=True, null=True, verbose_name='Date/time'),
51
        ),
52
    ]
chrono/agendas/models.py
1083 1083
        ('weekdays', _('Every weekdays (Monday to Friday)')),
1084 1084
    ]
1085 1085

  
1086
    WEEKDAY_CHOICES = [
1087
        (0, _('Mo')),
1088
        (1, _('Tu')),
1089
        (2, _('We')),
1090
        (3, _('Th')),
1091
        (4, _('Fr')),
1092
        (5, _('Sa')),
1093
        (6, _('Su')),
1094
    ]
1095

  
1096
    INTERVAL_CHOICES = [
1097
        (1, 'Every week'),
1098
        (2, 'Every two weeks'),
1099
        (3, 'Every three weeks'),
1100
    ]
1101

  
1086 1102
    agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE)
1087
    start_datetime = models.DateTimeField(_('Date/time'))
1103
    start_datetime = models.DateTimeField(_('Date/time'), null=True, blank=True)
1088 1104
    repeat = models.CharField(_('Repeat'), max_length=16, blank=True, choices=REPEAT_CHOICES)
1105
    recurrence_days = ArrayField(
1106
        models.IntegerField(choices=WEEKDAY_CHOICES),
1107
        verbose_name=_('Recurrence days'),
1108
        default=list,
1109
        blank=True,
1110
        null=True,
1111
    )
1112
    recurrence_hour = models.TimeField(_('Hour'), null=True, blank=True)
1113
    recurrence_start_date = models.DateField(_('Recurrence start date'), null=True, blank=True)
1114
    recurrence_weekly_interval = models.IntegerField(_('Repeat'), choices=INTERVAL_CHOICES, default=1)
1089 1115
    recurrence_rule = JSONField(_('Recurrence rule'), null=True)
1090 1116
    recurrence_end_date = models.DateField(_('Recurrence end date'), null=True, blank=True)
1091 1117
    primary_event = models.ForeignKey('self', null=True, on_delete=models.CASCADE, related_name='recurrences')
......
1136 1162

  
1137 1163
    def save(self, seen_slugs=None, *args, **kwargs):
1138 1164
        assert self.agenda.kind != 'virtual', "an event can't reference a virtual agenda"
1165
        assert self.start_datetime or self.recurrence_hour and self.recurrence_start_date
1139 1166
        assert not (self.slug and self.slug.isdigit()), 'slug cannot be a number'
1140 1167
        self.start_datetime = self.start_datetime.replace(second=0, microsecond=0)
1141 1168
        self.check_full()
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):
......
146 146
        fields = [
147 147
            'label',
148 148
            'start_datetime',
149
            'repeat',
150 149
            'duration',
151 150
            'places',
152 151
        ]
......
155 154
        }
156 155

  
157 156

  
157
FREQUENCY_CHOICES = (
158
    ('unique', _('Unique')),
159
    ('recurring', _('Recurring')),
160
)
161

  
162

  
158 163
class EventForm(forms.ModelForm):
159
    protected_fields = ('repeat', 'slug', 'start_datetime')
164
    protected_fields = ('slug', 'start_datetime')
165

  
166
    frequency = forms.ChoiceField(
167
        label=_('Event frequency'), widget=forms.RadioSelect, choices=FREQUENCY_CHOICES
168
    )
169
    recurrence_days = forms.TypedMultipleChoiceField(
170
        choices=Event.WEEKDAY_CHOICES, coerce=int, required=False, widget=WeekdaysWidget
171
    )
160 172

  
161 173
    class Meta:
162 174
        model = Event
163 175
        widgets = {
164 176
            'publication_date': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
177
            'recurrence_hour': forms.DateInput(attrs={'type': 'time'}, format='%H:%M'),
178
            'recurrence_start_date': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
165 179
            'recurrence_end_date': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
166 180
        }
167 181
        fields = [
168 182
            'label',
169 183
            'slug',
184
            'frequency',
170 185
            'start_datetime',
171
            'repeat',
186
            'recurrence_days',
187
            'recurrence_hour',
188
            'recurrence_weekly_interval',
189
            'recurrence_start_date',
172 190
            'recurrence_end_date',
173 191
            'duration',
174 192
            'publication_date',
......
184 202

  
185 203
    def __init__(self, *args, **kwargs):
186 204
        super().__init__(*args, **kwargs)
205
        self.fields['frequency'].initial = 'recurring' if self.instance.recurrence_days else 'unique'
187 206
        if self.instance.recurrence_rule and self.instance.has_recurrences_booked():
188 207
            for field in self.protected_fields:
189 208
                self.fields[field].disabled = True
......
191 210
                    'This field cannot be modified because some recurrences have bookings attached to them.'
192 211
                )
193 212
        if self.instance.primary_event:
194
            for field in ('slug', 'repeat', 'recurrence_end_date', 'publication_date'):
213
            for field in ('slug', 'recurrence_end_date', 'publication_date'):
195 214
                del self.fields[field]
196 215

  
197 216
    def clean(self):
......
200 219
            after=self.cleaned_data['recurrence_end_date']
201 220
        ):
202 221
            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.'))
205 222

  
206 223
    def save(self, *args, **kwargs):
207 224
        with transaction.atomic():
......
211 228
                update_fields = {
212 229
                    field: value
213 230
                    for field, value in self.cleaned_data.items()
214
                    if field not in self.protected_fields
231
                    if field != 'frequency' and field not in self.protected_fields
215 232
                }
216 233
                self.instance.recurrences.update(**update_fields)
217 234

  
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>
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/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'
62
-