Projet

Général

Profil

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

Valentin Deniaud, 15 mars 2022 13:39

Télécharger (10,9 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                       | 49 ++++++++++++++-----
 .../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, 83 insertions(+), 21 deletions(-)
chrono/agendas/models.py
30 30
from dateutil.rrule import DAILY, WEEKLY, rrule, rruleset
31 31
from django.conf import settings
32 32
from django.contrib.auth.models import Group
33
from django.contrib.humanize.templatetags.humanize import ordinal
33 34
from django.contrib.postgres.fields import ArrayField, JSONField
34 35
from django.core.exceptions import FieldDoesNotExist, ValidationError
35 36
from django.core.validators import MaxValueValidator, MinValueValidator
......
1218 1219
        ordering = ['weekday', 'start_time']
1219 1220

  
1220 1221
    def __str__(self):
1221
        return '%s / %s → %s' % (
1222
            force_text(WEEKDAYS[self.weekday]),
1222
        label = force_text(WEEKDAYS[self.weekday])
1223
        if self.weekday_indexes:
1224
            label = _('%(weekday)s (%(ordinals)s of the month)') % {
1225
                'weekday': label,
1226
                'ordinals': ', '.join(ordinal(i) for i in self.weekday_indexes),
1227
            }
1228

  
1229
        label = '%s / %s → %s' % (
1230
            label,
1223 1231
            date_format(self.start_time, 'TIME_FORMAT'),
1224 1232
            date_format(self.end_time, 'TIME_FORMAT'),
1225 1233
        )
1234
        return mark_safe(label)
1226 1235

  
1227 1236
    def save(self, *args, **kwargs):
1228 1237
        if self.agenda:
chrono/manager/forms.py
36 36
from django.utils.translation import ugettext_lazy as _
37 37

  
38 38
from chrono.agendas.models import (
39
    WEEK_CHOICES,
39 40
    WEEKDAY_CHOICES,
40 41
    WEEKDAYS_LIST,
41 42
    AbsenceReason,
......
699 700
                    )
700 701

  
701 702

  
702
class TimePeriodAddForm(forms.Form):
703
class TimePeriodFormBase(forms.Form):
704
    repeat = forms.ChoiceField(
705
        label=_('Repeat'),
706
        widget=forms.RadioSelect,
707
        choices=(
708
            ('every-week', _('Every week')),
709
            ('custom', _('Custom')),
710
        ),
711
        initial='every-week',
712
    )
713
    weekday_indexes = forms.TypedMultipleChoiceField(
714
        choices=WEEK_CHOICES,
715
        coerce=int,
716
        required=False,
717
        label='',
718
    )
719

  
720
    def clean(self):
721
        cleaned_data = super().clean()
722

  
723
        if cleaned_data['end_time'] <= cleaned_data['start_time']:
724
            raise ValidationError(_('End time must come after start time.'))
725

  
726
        if cleaned_data['repeat'] == 'every-week':
727
            cleaned_data['weekday_indexes'] = None
728

  
729
        return cleaned_data
730

  
731

  
732
class TimePeriodAddForm(TimePeriodFormBase):
733
    field_order = ['weekdays', 'start_time', 'end_time', 'repeat', 'weekday_indexes']
734

  
703 735
    weekdays = forms.MultipleChoiceField(
704 736
        label=_('Days'), widget=forms.CheckboxSelectMultiple(), choices=WEEKDAYS_LIST
705 737
    )
706 738
    start_time = forms.TimeField(label=_('Start Time'), widget=widgets.TimeWidget())
707 739
    end_time = forms.TimeField(label=_('End Time'), widget=widgets.TimeWidget())
708 740

  
709
    def clean_end_time(self):
710
        if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']:
711
            raise ValidationError(_('End time must come after start time.'))
712
        return self.cleaned_data['end_time']
713 741

  
714

  
715
class TimePeriodForm(forms.ModelForm):
742
class TimePeriodForm(TimePeriodFormBase, forms.ModelForm):
716 743
    class Meta:
717 744
        model = TimePeriod
718 745
        widgets = {
719 746
            'start_time': widgets.TimeWidget(),
720 747
            'end_time': widgets.TimeWidget(),
721 748
        }
722
        exclude = ['agenda', 'desk']
749
        fields = ['weekday', 'start_time', 'end_time', 'repeat', 'weekday_indexes']
723 750

  
724 751
    def __init__(self, *args, **kwargs):
725 752
        super().__init__(*args, **kwargs)
......
727 754
        self.old_start_time = self.instance.start_time
728 755
        self.old_end_time = self.instance.end_time
729 756

  
730
    def clean_end_time(self):
731
        if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']:
732
            raise ValidationError(_('End time must come after start time.'))
733
        return self.cleaned_data['end_time']
757
        if self.instance.weekday_indexes:
758
            self.fields['repeat'].initial = 'custom'
734 759

  
735 760
    def save(self):
736 761
        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
2494 2494
            weekday=weekday,
2495 2495
            start_time=form.cleaned_data['start_time'],
2496 2496
            end_time=form.cleaned_data['end_time'],
2497
            weekday_indexes=form.cleaned_data['weekday_indexes'],
2497 2498
        )
2498 2499
        if desk:
2499 2500
            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
-