Projet

Général

Profil

0003-manager-choose-time-period-weekday-indexes-45159.patch

Valentin Deniaud, 10 mars 2022 16:02

Télécharger (11 ko)

Voir les différences:

Subject: [PATCH 3/5] manager: choose time period weekday indexes (#45159)

 chrono/agendas/models.py                      | 13 +++++-
 chrono/manager/forms.py                       | 46 ++++++++++++++-----
 .../manager_meetings_agenda_settings.html     |  3 +-
 .../chrono/manager_time_period_form.html      | 16 ++++++-
 .../manager_virtual_agenda_settings.html      |  3 +-
 chrono/manager/views.py                       |  1 +
 chrono/settings.py                            |  1 +
 tests/manager/test_timeperiod.py              | 18 +++++++-
 8 files changed, 81 insertions(+), 20 deletions(-)
chrono/agendas/models.py
29 29
from dateutil.rrule import DAILY, WEEKLY, rrule, rruleset
30 30
from django.conf import settings
31 31
from django.contrib.auth.models import Group
32
from django.contrib.humanize.templatetags.humanize import ordinal
32 33
from django.contrib.postgres.fields import ArrayField, JSONField
33 34
from django.core.exceptions import FieldDoesNotExist, ValidationError
34 35
from django.core.validators import MaxValueValidator, MinValueValidator
......
1185 1186
        ordering = ['weekday', 'start_time']
1186 1187

  
1187 1188
    def __str__(self):
1188
        return '%s / %s → %s' % (
1189
            force_text(WEEKDAYS[self.weekday]),
1189
        label = force_text(WEEKDAYS[self.weekday])
1190
        if self.weekday_indexes:
1191
            label = _('%(weekday)s (%(ordinals)s of the month)') % {
1192
                'weekday': label,
1193
                'ordinals': ', '.join(ordinal(i) for i in self.weekday_indexes),
1194
            }
1195

  
1196
        label = '%s / %s → %s' % (
1197
            label,
1190 1198
            date_format(self.start_time, 'TIME_FORMAT'),
1191 1199
            date_format(self.end_time, 'TIME_FORMAT'),
1192 1200
        )
1201
        return mark_safe(label)
1193 1202

  
1194 1203
    def save(self, *args, **kwargs):
1195 1204
        if self.agenda:
chrono/manager/forms.py
35 35
from django.utils.translation import ugettext_lazy as _
36 36

  
37 37
from chrono.agendas.models import (
38
    WEEK_CHOICES,
38 39
    WEEKDAYS_LIST,
39 40
    AbsenceReason,
40 41
    AbsenceReasonGroup,
......
694 695
                    )
695 696

  
696 697

  
697
class TimePeriodAddForm(forms.Form):
698
    weekdays = forms.MultipleChoiceField(
699
        label=_('Days'), widget=forms.CheckboxSelectMultiple(), choices=WEEKDAYS_LIST
698
class TimePeriodFormBase(forms.Form):
699
    repeat = forms.ChoiceField(
700
        label=_('Repeat'),
701
        widget=forms.RadioSelect,
702
        choices=(
703
            ('every-week', _('Every week')),
704
            ('custom', _('Custom')),
705
        ),
706
        initial='every-week',
707
    )
708
    weekday_indexes = forms.TypedMultipleChoiceField(
709
        choices=WEEK_CHOICES,
710
        coerce=int,
711
        required=False,
712
        label='',
700 713
    )
701
    start_time = forms.TimeField(label=_('Start Time'), widget=widgets.TimeWidget())
702
    end_time = forms.TimeField(label=_('End Time'), widget=widgets.TimeWidget())
703 714

  
704 715
    def clean_end_time(self):
705 716
        if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']:
706 717
            raise ValidationError(_('End time must come after start time.'))
707 718
        return self.cleaned_data['end_time']
708 719

  
720
    def clean_weekday_indexes(self):
721
        if self.cleaned_data['repeat'] == 'every-week':
722
            return None
723
        return self.cleaned_data['weekday_indexes']
724

  
709 725

  
710
class TimePeriodForm(forms.ModelForm):
726
class TimePeriodAddForm(TimePeriodFormBase):
727
    field_order = ['weekdays', 'start_time', 'end_time', 'repeat', 'weekday_indexes']
728

  
729
    weekdays = forms.MultipleChoiceField(
730
        label=_('Days'), widget=forms.CheckboxSelectMultiple(), choices=WEEKDAYS_LIST
731
    )
732
    start_time = forms.TimeField(label=_('Start Time'), widget=widgets.TimeWidget())
733
    end_time = forms.TimeField(label=_('End Time'), widget=widgets.TimeWidget())
734

  
735

  
736
class TimePeriodForm(TimePeriodFormBase, forms.ModelForm):
711 737
    class Meta:
712 738
        model = TimePeriod
713 739
        widgets = {
714 740
            'start_time': widgets.TimeWidget(),
715 741
            'end_time': widgets.TimeWidget(),
716 742
        }
717
        exclude = ['agenda', 'desk']
743
        fields = ['weekday', 'start_time', 'end_time', 'repeat', 'weekday_indexes']
718 744

  
719 745
    def __init__(self, *args, **kwargs):
720 746
        super().__init__(*args, **kwargs)
......
722 748
        self.old_start_time = self.instance.start_time
723 749
        self.old_end_time = self.instance.end_time
724 750

  
725
    def clean_end_time(self):
726
        if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']:
727
            raise ValidationError(_('End time must come after start time.'))
728
        return self.cleaned_data['end_time']
751
        if self.instance.weekday_indexes:
752
            self.fields['repeat'].initial = 'custom'
729 753

  
730 754
    def save(self):
731 755
        super().save()
chrono/manager/templates/chrono/manager_meetings_agenda_settings.html
88 88
                </li>
89 89
                {% endif %}
90 90
                {% for time_period in desk.timeperiod_set.all %}
91
                <li><a rel="popup" href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}">
92
                {{time_period.weekday_str}} / {{time_period.start_time}} → {{time_period.end_time}}</a>
91
                <li><a rel="popup" href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}">{{ time_period }}</a>
93 92
                <a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-delete' pk=time_period.id %}">{% trans "remove" %}</a>
94 93

  
95 94
                </li>
chrono/manager/templates/chrono/manager_time_period_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 }}
......
30 30

  
31 31
<form method="post" enctype="multipart/form-data">
32 32
  {% csrf_token %}
33
  {{ form.as_p }}
33
  {{ form|with_template }}
34 34
  <div class="buttons">
35 35
    <button class="submit-button">{% trans "Save" %}</button>
36 36
    <a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Cancel' %}</a>
37 37
  </div>
38

  
39
  <script>
40
    $('input[type=radio][name=repeat]').change(function() {
41
      if(!this.checked)
42
        return;
43
      if(this.value == 'every-week') {
44
        $('select#id_weekday_indexes').hide();
45
      } else {
46
        $('select#id_weekday_indexes').show();
47
      }
48
    }).change();
49
  </script>
38 50
</form>
39 51
{% endblock %}
chrono/manager/templates/chrono/manager_virtual_agenda_settings.html
66 66
<div>
67 67
  <ul class="objects-list single-links">
68 68
    {% for time_period in agenda.excluded_timeperiods.all %}
69
    <li><a rel="popup" href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}">
70
        {{time_period.weekday_str}} / {{time_period.start_time}} → {{time_period.end_time}}</a>
69
    <li><a rel="popup" href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}">{{ time_period }}</a>
71 70
      <a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-delete' pk=time_period.id %}">{% trans "remove" %}</a>
72 71
    </li>
73 72
    {% endfor %}
chrono/manager/views.py
2445 2445
            weekday=weekday,
2446 2446
            start_time=form.cleaned_data['start_time'],
2447 2447
            end_time=form.cleaned_data['end_time'],
2448
            weekday_indexes=form.cleaned_data['weekday_indexes'],
2448 2449
        )
2449 2450
        if desk:
2450 2451
            period.desk = desk
chrono/settings.py
52 52
    'django.contrib.sessions',
53 53
    'django.contrib.messages',
54 54
    'django.contrib.staticfiles',
55
    'django.contrib.humanize',
55 56
    'gadjo',
56 57
    'chrono.agendas',
57 58
    'chrono.api',
tests/manager/test_timeperiod.py
25 25
    assert TimePeriod.objects.get(desk=desk).start_time.minute == 0
26 26
    assert TimePeriod.objects.get(desk=desk).end_time.hour == 17
27 27
    assert TimePeriod.objects.get(desk=desk).end_time.minute == 0
28
    assert TimePeriod.objects.get(desk=desk).weekday_indexes is None
28 29
    assert desk2.timeperiod_set.exists() is False
29 30
    resp = resp.follow()
30 31

  
......
33 34
    resp.form.get('weekdays', index=0).checked = True
34 35
    resp.form['start_time'] = '10:00'
35 36
    resp.form['end_time'] = '13:00'
37
    resp.form['repeat'] = 'custom'
38
    resp.form['weekday_indexes'] = [1, 3]
36 39
    resp = resp.form.submit()
37 40
    resp = resp.follow()
38
    assert 'Monday / 10 a.m. → 1 p.m.' in resp.text
41
    assert 'Monday (1st, 3rd of the month) / 10 a.m. → 1 p.m.' in resp.text
39 42
    assert 'Wednesday / 10 a.m. → 5 p.m.' in resp.text
40 43
    assert resp.text.index('Monday') < resp.text.index('Wednesday')
41 44

  
......
134 137
    time_period2.refresh_from_db()
135 138
    assert time_period2.start_time.hour == 9
136 139

  
140
    resp = resp.click('Monday / 10 a.m. → noon')
141
    resp.form['repeat'] = 'custom'
142
    resp.form['weekday_indexes'] = [5]
143
    resp = resp.form.submit().follow()
144
    time_period.refresh_from_db()
145
    assert time_period.weekday_indexes == [5]
146

  
147
    resp = resp.click('Monday \\(5th of the month\\) / 10 a.m. → noon')
148
    resp.form['repeat'] = 'every-week'
149
    resp = resp.form.submit().follow()
150
    time_period.refresh_from_db()
151
    assert time_period.weekday_indexes is None
152

  
137 153
    # edit with inverted start/end
138 154
    resp2 = resp.click('Monday / 10 a.m. → noon')
139 155
    resp2.form['start_time'] = '18:00'
140
-