manager: add excluded timeperiods management (#40058)

This commit is contained in:
Emmanuel Cazenave 2020-02-26 11:52:05 +01:00 committed by Frédéric Péters
parent 3aba16e82b
commit aec5b30fd9
6 changed files with 154 additions and 12 deletions

View File

@ -150,9 +150,18 @@ class TimePeriodForm(forms.ModelForm):
'start_time': widgets.TimeWidget(),
'end_time': widgets.TimeWidget(),
'desk': forms.HiddenInput(),
'agenda': forms.HiddenInput(),
}
exclude = []
def __init__(self, *args, **kwargs):
has_desk = kwargs.pop('has_desk')
super(TimePeriodForm, self).__init__(*args, **kwargs)
if has_desk:
del self.fields['agenda']
else:
del self.fields['desk']
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.'))

View File

@ -33,7 +33,7 @@
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Save" %}</button>
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=desk.agenda.id %}">{% trans 'Cancel' %}</a>
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -2,6 +2,7 @@
{% load i18n %}
{% block agenda-extra-management-actions %}
<a rel="popup" href="{% url 'chrono-manager-virtual-agenda-add-time-period' pk=object.id %}">{% trans 'Add Excluded Period' %}</a>
<a rel="popup" href="{% url 'chrono-manager-agenda-add-virtual-member' pk=object.id %}">{% trans 'Include Agenda' %}</a>
{% endblock %}
@ -59,5 +60,20 @@
</div>
{% endif %}
{% if agenda.excluded_timeperiods.count %}
<div class="section">
<h3>{% trans 'Excluded Periods' %}</h3>
<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>
<a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-delete' pk=time_period.id %}">{% trans "remove" %}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -74,6 +74,11 @@ urlpatterns = [
views.agenda_add_time_period,
name='chrono-manager-agenda-add-time-period',
),
url(
r'^agendas/(?P<pk>\d+)/add-time-period$',
views.virtual_agenda_add_time_period,
name='chrono-manager-virtual-agenda-add-time-period',
),
url(r'^timeperiods/(?P<pk>\d+)/edit$', views.time_period_edit, name='chrono-manager-time-period-edit'),
url(
r'^timeperiods/(?P<pk>\d+)/delete$',

View File

@ -680,6 +680,30 @@ class ManagedDeskSubobjectMixin(object):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id})
class ManagedTimePeriodMixin(object):
agenda = None
def dispatch(self, request, *args, **kwargs):
self.time_period = self.get_object()
self.agenda = self.time_period.agenda
self.has_desk = False
if self.time_period.desk:
self.agenda = self.time_period.desk.agenda
self.has_desk = True
if not self.agenda.can_be_managed(request.user):
raise PermissionDenied()
return super(ManagedTimePeriodMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ManagedTimePeriodMixin, self).get_context_data(**kwargs)
context['agenda'] = self.agenda
return context
def get_success_url(self):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id})
class AgendaSettings(ManagedAgendaMixin, DetailView):
model = Agenda
@ -891,35 +915,60 @@ class MeetingTypeDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
meeting_type_delete = MeetingTypeDeleteView.as_view()
def process_time_period_add_form(form, desk=None, agenda=None):
assert desk or agenda, "a time period requires a desk or a agenda"
for weekday in form.cleaned_data.get('weekdays'):
period = TimePeriod(
weekday=weekday,
start_time=form.cleaned_data['start_time'],
end_time=form.cleaned_data['end_time'],
)
if desk:
period.desk = desk
elif agenda:
period.agenda = agenda
period.save()
class AgendaAddTimePeriodView(ManagedDeskMixin, FormView):
template_name = 'chrono/manager_time_period_form.html'
form_class = TimePeriodAddForm
def form_valid(self, form):
for weekday in form.cleaned_data.get('weekdays'):
period = TimePeriod(
weekday=weekday,
start_time=form.cleaned_data['start_time'],
end_time=form.cleaned_data['end_time'],
desk=self.desk,
)
period.save()
process_time_period_add_form(form, desk=self.desk)
return super(AgendaAddTimePeriodView, self).form_valid(form)
agenda_add_time_period = AgendaAddTimePeriodView.as_view()
class TimePeriodEditView(ManagedDeskSubobjectMixin, UpdateView):
class VirtualAgendaAddTimePeriodView(ManagedAgendaMixin, FormView):
template_name = 'chrono/manager_time_period_form.html'
form_class = TimePeriodAddForm
def form_valid(self, form):
process_time_period_add_form(form, agenda=self.agenda)
return super(VirtualAgendaAddTimePeriodView, self).form_valid(form)
virtual_agenda_add_time_period = VirtualAgendaAddTimePeriodView.as_view()
class TimePeriodEditView(ManagedTimePeriodMixin, UpdateView):
template_name = 'chrono/manager_time_period_form.html'
model = TimePeriod
form_class = TimePeriodForm
def get_form_kwargs(self):
kwargs = super(TimePeriodEditView, self).get_form_kwargs()
kwargs['has_desk'] = self.has_desk
return kwargs
time_period_edit = TimePeriodEditView.as_view()
class TimePeriodDeleteView(ManagedDeskSubobjectMixin, DeleteView):
class TimePeriodDeleteView(ManagedTimePeriodMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = TimePeriod

View File

@ -2251,9 +2251,12 @@ def test_virtual_agenda_settings_empty(app, admin_user):
assert 'Export' in resp.text
assert 'Delete' in resp.text
assert 'Included Agendas' in resp.text
assert 'Add Excluded Period' in resp.text
assert "This virtual agenda doesn't include any agenda yet" in resp.text
# No meeting types yet
assert 'Meeting Types' not in resp.text
# No absence yet
assert 'Excluded Periods' not in resp.text
def test_virtual_agenda_settings(app, admin_user):
@ -2264,7 +2267,9 @@ def test_virtual_agenda_settings(app, admin_user):
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
TimePeriod.objects.create(
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert "This virtual agenda doesn't include any agenda yet" not in resp.text
@ -2277,6 +2282,9 @@ def test_virtual_agenda_settings(app, admin_user):
assert 'mt' in resp.text
assert '10' in resp.text
assert 'Excluded Periods' in resp.text
assert 'Monday' in resp.text
# Error message when incompatible meeting types
mt2.delete()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
@ -2307,6 +2315,61 @@ def test_virtual_agenda_settings_include(app, admin_user):
assert len(resp.form['real_agenda'].options) == 2
def test_virtual_agenda_settings_add_excluded_period(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Add Excluded Period')
resp.form['weekdays-0'].checked = True
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '17:00'
resp = resp.form.submit()
tp = TimePeriod.objects.get(agenda=agenda)
assert tp.weekday == 0
assert tp.start_time.hour == 10
assert tp.start_time.minute == 0
assert tp.end_time.hour == 17
assert tp.end_time.minute == 0
resp = resp.follow()
assert u'Monday / 10 a.m. → 5 p.m.' in resp.text
def test_virtual_agenda_settings_edit_excluded_period(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
tp = TimePeriod.objects.create(
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
url = '/manage/timeperiods/%s/edit' % tp.pk
resp = resp.click(href=url)
resp.form['start_time'] = '11:00'
resp = resp.form.submit()
tp = TimePeriod.objects.get(agenda=agenda)
assert tp.weekday == 0
assert tp.start_time.hour == 11
assert tp.start_time.minute == 0
assert tp.end_time.hour == 18
assert tp.end_time.minute == 0
def test_virtual_agenda_settings_delete_excluded_period(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
tp = TimePeriod.objects.create(
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
url = '/manage/timeperiods/%s/delete' % tp.pk
resp = resp.click(href=url)
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert TimePeriod.objects.count() == 0
def test_virtual_agenda_settings_include_incompatible_agenda(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')