manager: choose time period weekday indexes (#45159)

This commit is contained in:
Valentin Deniaud 2022-03-10 15:03:54 +01:00
parent 6c067790e1
commit 956c1fd183
8 changed files with 83 additions and 21 deletions

View File

@ -30,6 +30,7 @@ import vobject
from dateutil.rrule import DAILY, WEEKLY, rrule, rruleset
from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.humanize.templatetags.humanize import ordinal
from django.contrib.postgres.fields import ArrayField, JSONField
from django.core.exceptions import FieldDoesNotExist, ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
@ -1288,11 +1289,19 @@ class TimePeriod(models.Model):
ordering = ['weekday', 'start_time']
def __str__(self):
return '%s / %s%s' % (
force_text(WEEKDAYS[self.weekday]),
label = force_text(WEEKDAYS[self.weekday])
if self.weekday_indexes:
label = _('%(weekday)s (%(ordinals)s of the month)') % {
'weekday': label,
'ordinals': ', '.join(ordinal(i) for i in self.weekday_indexes),
}
label = '%s / %s%s' % (
label,
date_format(self.start_time, 'TIME_FORMAT'),
date_format(self.end_time, 'TIME_FORMAT'),
)
return mark_safe(label)
def save(self, *args, **kwargs):
if self.agenda:

View File

@ -36,6 +36,7 @@ from django.utils.timezone import localtime, make_aware, now
from django.utils.translation import ugettext_lazy as _
from chrono.agendas.models import (
WEEK_CHOICES,
WEEKDAY_CHOICES,
WEEKDAYS_LIST,
AbsenceReason,
@ -699,27 +700,53 @@ class MeetingTypeForm(forms.ModelForm):
)
class TimePeriodAddForm(forms.Form):
class TimePeriodFormBase(forms.Form):
repeat = forms.ChoiceField(
label=_('Repeat'),
widget=forms.RadioSelect,
choices=(
('every-week', _('Every week')),
('custom', _('Custom')),
),
initial='every-week',
)
weekday_indexes = forms.TypedMultipleChoiceField(
choices=WEEK_CHOICES,
coerce=int,
required=False,
label='',
)
def clean(self):
cleaned_data = super().clean()
if cleaned_data['end_time'] <= cleaned_data['start_time']:
raise ValidationError(_('End time must come after start time.'))
if cleaned_data['repeat'] == 'every-week':
cleaned_data['weekday_indexes'] = None
return cleaned_data
class TimePeriodAddForm(TimePeriodFormBase):
field_order = ['weekdays', 'start_time', 'end_time', 'repeat', 'weekday_indexes']
weekdays = forms.MultipleChoiceField(
label=_('Days'), widget=forms.CheckboxSelectMultiple(), choices=WEEKDAYS_LIST
)
start_time = forms.TimeField(label=_('Start Time'), widget=widgets.TimeWidget())
end_time = forms.TimeField(label=_('End Time'), widget=widgets.TimeWidget())
def clean_end_time(self):
if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']:
raise ValidationError(_('End time must come after start time.'))
return self.cleaned_data['end_time']
class TimePeriodForm(forms.ModelForm):
class TimePeriodForm(TimePeriodFormBase, forms.ModelForm):
class Meta:
model = TimePeriod
widgets = {
'start_time': widgets.TimeWidget(),
'end_time': widgets.TimeWidget(),
}
exclude = ['agenda', 'desk']
fields = ['weekday', 'start_time', 'end_time', 'repeat', 'weekday_indexes']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -727,10 +754,8 @@ class TimePeriodForm(forms.ModelForm):
self.old_start_time = self.instance.start_time
self.old_end_time = self.instance.end_time
def clean_end_time(self):
if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']:
raise ValidationError(_('End time must come after start time.'))
return self.cleaned_data['end_time']
if self.instance.weekday_indexes:
self.fields['repeat'].initial = 'custom'
def save(self):
super().save()

View File

@ -88,8 +88,7 @@
</li>
{% endif %}
{% for time_period in desk.timeperiod_set.all %}
<li><a rel="popup" href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}">
{{time_period.weekday_str}} / {{time_period.start_time}} → {{time_period.end_time}}</a>
<li><a rel="popup" href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}">{{ time_period }}</a>
<a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-delete' pk=time_period.id %}">{% trans "remove" %}</a>
</li>

View File

@ -1,5 +1,5 @@
{% extends "chrono/manager_agenda_view.html" %}
{% load i18n %}
{% load i18n gadjo %}
{% block extrascripts %}
{{ block.super }}
@ -30,10 +30,22 @@
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ form|with_template }}
<div class="buttons">
<button class="submit-button">{% trans "Save" %}</button>
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Cancel' %}</a>
</div>
<script>
$('input[type=radio][name=repeat]').change(function() {
if(!this.checked)
return;
if(this.value == 'every-week') {
$('select#id_weekday_indexes').hide();
} else {
$('select#id_weekday_indexes').show();
}
}).change();
</script>
</form>
{% endblock %}

View File

@ -66,8 +66,7 @@
<div>
<ul class="objects-list single-links">
{% for time_period in agenda.excluded_timeperiods.all %}
<li><a rel="popup" href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}">
{{time_period.weekday_str}} / {{time_period.start_time}} → {{time_period.end_time}}</a>
<li><a rel="popup" href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}">{{ time_period }}</a>
<a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-delete' pk=time_period.id %}">{% trans "remove" %}</a>
</li>
{% endfor %}

View File

@ -2494,6 +2494,7 @@ def process_time_period_add_form(form, desk=None, agenda=None):
weekday=weekday,
start_time=form.cleaned_data['start_time'],
end_time=form.cleaned_data['end_time'],
weekday_indexes=form.cleaned_data['weekday_indexes'],
)
if desk:
period.desk = desk

View File

@ -52,6 +52,7 @@ INSTALLED_APPS = (
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'gadjo',
'chrono.agendas',
'chrono.api',

View File

@ -25,6 +25,7 @@ def test_meetings_agenda_add_time_period(app, admin_user):
assert TimePeriod.objects.get(desk=desk).start_time.minute == 0
assert TimePeriod.objects.get(desk=desk).end_time.hour == 17
assert TimePeriod.objects.get(desk=desk).end_time.minute == 0
assert TimePeriod.objects.get(desk=desk).weekday_indexes is None
assert desk2.timeperiod_set.exists() is False
resp = resp.follow()
@ -33,9 +34,11 @@ def test_meetings_agenda_add_time_period(app, admin_user):
resp.form.get('weekdays', index=0).checked = True
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '13:00'
resp.form['repeat'] = 'custom'
resp.form['weekday_indexes'] = [1, 3]
resp = resp.form.submit()
resp = resp.follow()
assert 'Monday / 10 a.m. → 1 p.m.' in resp.text
assert 'Monday (1st, 3rd of the month) / 10 a.m. → 1 p.m.' in resp.text
assert 'Wednesday / 10 a.m. → 5 p.m.' in resp.text
assert resp.text.index('Monday') < resp.text.index('Wednesday')
@ -134,6 +137,19 @@ def test_meetings_agenda_edit_time_period(app, admin_user):
time_period2.refresh_from_db()
assert time_period2.start_time.hour == 9
resp = resp.click('Monday / 10 a.m. → noon')
resp.form['repeat'] = 'custom'
resp.form['weekday_indexes'] = [5]
resp = resp.form.submit().follow()
time_period.refresh_from_db()
assert time_period.weekday_indexes == [5]
resp = resp.click('Monday \\(5th of the month\\) / 10 a.m. → noon')
resp.form['repeat'] = 'every-week'
resp = resp.form.submit().follow()
time_period.refresh_from_db()
assert time_period.weekday_indexes is None
# edit with inverted start/end
resp2 = resp.click('Monday / 10 a.m. → noon')
resp2.form['start_time'] = '18:00'