0007-manager-add-excluded-timeperiods-management-40058.patch
chrono/manager/forms.py | ||
---|---|---|
150 | 150 |
'start_time': widgets.TimeWidget(), |
151 | 151 |
'end_time': widgets.TimeWidget(), |
152 | 152 |
'desk': forms.HiddenInput(), |
153 |
'agenda': forms.HiddenInput(), |
|
153 | 154 |
} |
154 | 155 |
exclude = [] |
155 | 156 | |
157 |
def __init__(self, *args, **kwargs): |
|
158 |
has_desk = kwargs.pop('has_desk') |
|
159 |
super(TimePeriodForm, self).__init__(*args, **kwargs) |
|
160 |
if has_desk: |
|
161 |
del self.fields['agenda'] |
|
162 |
else: |
|
163 |
del self.fields['desk'] |
|
164 | ||
156 | 165 |
def clean_end_time(self): |
157 | 166 |
if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']: |
158 | 167 |
raise ValidationError(_('End time must come after start time.')) |
chrono/manager/templates/chrono/manager_time_period_form.html | ||
---|---|---|
33 | 33 |
{{ form.as_p }} |
34 | 34 |
<div class="buttons"> |
35 | 35 |
<button class="submit-button">{% trans "Save" %}</button> |
36 |
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=desk.agenda.id %}">{% trans 'Cancel' %}</a>
|
|
36 |
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Cancel' %}</a> |
|
37 | 37 |
</div> |
38 | 38 |
</form> |
39 | 39 |
{% endblock %} |
chrono/manager/templates/chrono/manager_virtual_agenda_settings.html | ||
---|---|---|
2 | 2 |
{% load i18n %} |
3 | 3 | |
4 | 4 |
{% block agenda-extra-management-actions %} |
5 |
<a rel="popup" href="{% url 'chrono-manager-virtual-agenda-add-time-period' pk=object.id %}">{% trans 'Add Excluded Period' %}</a> |
|
5 | 6 |
<a rel="popup" href="{% url 'chrono-manager-agenda-add-virtual-member' pk=object.id %}">{% trans 'Include Agenda' %}</a> |
6 | 7 |
{% endblock %} |
7 | 8 | |
... | ... | |
60 | 61 |
</div> |
61 | 62 |
{% endif %} |
62 | 63 | |
64 |
{% if agenda.excluded_timeperiods.count %} |
|
65 |
<div class="section"> |
|
66 |
<h3>{% trans 'Excluded Periods' %}</h3> |
|
67 |
<div> |
|
68 |
<ul class="objects-list single-links"> |
|
69 |
{% for time_period in agenda.excluded_timeperiods.all %} |
|
70 |
<li><a rel="popup" href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}"> |
|
71 |
{{time_period.weekday_str}} / {{time_period.start_time}} → {{time_period.end_time}}</a> |
|
72 |
<a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-delete' pk=time_period.id %}">{% trans "remove" %}</a> |
|
73 |
</li> |
|
74 |
{% endfor %} |
|
75 |
</ul> |
|
76 |
</div> |
|
77 |
</div> |
|
78 |
{% endif %} |
|
63 | 79 | |
64 | 80 |
{% endblock %} |
chrono/manager/urls.py | ||
---|---|---|
74 | 74 |
views.agenda_add_time_period, |
75 | 75 |
name='chrono-manager-agenda-add-time-period', |
76 | 76 |
), |
77 |
url( |
|
78 |
r'^agendas/(?P<pk>\d+)/add-time-period$', |
|
79 |
views.virtual_agenda_add_time_period, |
|
80 |
name='chrono-manager-virtual-agenda-add-time-period', |
|
81 |
), |
|
77 | 82 |
url(r'^timeperiods/(?P<pk>\d+)/edit$', views.time_period_edit, name='chrono-manager-time-period-edit'), |
78 | 83 |
url( |
79 | 84 |
r'^timeperiods/(?P<pk>\d+)/delete$', |
chrono/manager/views.py | ||
---|---|---|
676 | 676 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id}) |
677 | 677 | |
678 | 678 | |
679 |
class ManagedTimePeriodMixin(object): |
|
680 |
agenda = None |
|
681 | ||
682 |
def dispatch(self, request, *args, **kwargs): |
|
683 |
self.time_period = self.get_object() |
|
684 |
self.agenda = self.time_period.agenda |
|
685 |
self.has_desk = False |
|
686 |
if self.time_period.desk: |
|
687 |
self.agenda = self.time_period.desk.agenda |
|
688 |
self.has_desk = True |
|
689 | ||
690 |
if not self.agenda.can_be_managed(request.user): |
|
691 |
raise PermissionDenied() |
|
692 |
return super(ManagedTimePeriodMixin, self).dispatch(request, *args, **kwargs) |
|
693 | ||
694 |
def get_context_data(self, **kwargs): |
|
695 |
context = super(ManagedTimePeriodMixin, self).get_context_data(**kwargs) |
|
696 |
context['agenda'] = self.agenda |
|
697 |
return context |
|
698 | ||
699 |
def get_success_url(self): |
|
700 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) |
|
701 | ||
702 | ||
679 | 703 |
class AgendaSettings(ManagedAgendaMixin, DetailView): |
680 | 704 |
model = Agenda |
681 | 705 | |
... | ... | |
884 | 908 |
meeting_type_delete = MeetingTypeDeleteView.as_view() |
885 | 909 | |
886 | 910 | |
911 |
def process_time_period_add_form(form, desk=None, agenda=None): |
|
912 |
assert desk or agenda, "a time period requires a desk or a agenda" |
|
913 |
for weekday in form.cleaned_data.get('weekdays'): |
|
914 |
period = TimePeriod( |
|
915 |
weekday=weekday, |
|
916 |
start_time=form.cleaned_data['start_time'], |
|
917 |
end_time=form.cleaned_data['end_time'], |
|
918 |
) |
|
919 |
if desk: |
|
920 |
period.desk = desk |
|
921 |
elif agenda: |
|
922 |
period.agenda = agenda |
|
923 |
period.save() |
|
924 | ||
925 | ||
887 | 926 |
class AgendaAddTimePeriodView(ManagedDeskMixin, FormView): |
888 | 927 |
template_name = 'chrono/manager_time_period_form.html' |
889 | 928 |
form_class = TimePeriodAddForm |
890 | 929 | |
891 | 930 |
def form_valid(self, form): |
892 |
for weekday in form.cleaned_data.get('weekdays'): |
|
893 |
period = TimePeriod( |
|
894 |
weekday=weekday, |
|
895 |
start_time=form.cleaned_data['start_time'], |
|
896 |
end_time=form.cleaned_data['end_time'], |
|
897 |
desk=self.desk, |
|
898 |
) |
|
899 |
period.save() |
|
931 |
process_time_period_add_form(form, desk=self.desk) |
|
900 | 932 |
return super(AgendaAddTimePeriodView, self).form_valid(form) |
901 | 933 | |
902 | 934 | |
903 | 935 |
agenda_add_time_period = AgendaAddTimePeriodView.as_view() |
904 | 936 | |
905 | 937 | |
906 |
class TimePeriodEditView(ManagedDeskSubobjectMixin, UpdateView): |
|
938 |
class VirtualAgendaAddTimePeriodView(ManagedAgendaMixin, FormView): |
|
939 |
template_name = 'chrono/manager_time_period_form.html' |
|
940 |
form_class = TimePeriodAddForm |
|
941 | ||
942 |
def form_valid(self, form): |
|
943 |
process_time_period_add_form(form, agenda=self.agenda) |
|
944 |
return super(VirtualAgendaAddTimePeriodView, self).form_valid(form) |
|
945 | ||
946 | ||
947 |
virtual_agenda_add_time_period = VirtualAgendaAddTimePeriodView.as_view() |
|
948 | ||
949 | ||
950 |
class TimePeriodEditView(ManagedTimePeriodMixin, UpdateView): |
|
907 | 951 |
template_name = 'chrono/manager_time_period_form.html' |
908 | 952 |
model = TimePeriod |
909 | 953 |
form_class = TimePeriodForm |
910 | 954 | |
955 |
def get_form_kwargs(self): |
|
956 |
kwargs = super(TimePeriodEditView, self).get_form_kwargs() |
|
957 |
kwargs['has_desk'] = self.has_desk |
|
958 |
return kwargs |
|
959 | ||
911 | 960 | |
912 | 961 |
time_period_edit = TimePeriodEditView.as_view() |
913 | 962 | |
914 | 963 | |
915 |
class TimePeriodDeleteView(ManagedDeskSubobjectMixin, DeleteView):
|
|
964 |
class TimePeriodDeleteView(ManagedTimePeriodMixin, DeleteView):
|
|
916 | 965 |
template_name = 'chrono/manager_confirm_delete.html' |
917 | 966 |
model = TimePeriod |
918 | 967 |
tests/test_manager.py | ||
---|---|---|
2252 | 2252 |
assert 'Export' in resp.text |
2253 | 2253 |
assert 'Delete' in resp.text |
2254 | 2254 |
assert 'Included Agendas' in resp.text |
2255 |
assert 'Add Excluded Period' in resp.text |
|
2255 | 2256 |
assert "This virtual agenda doesn't include any agenda yet" in resp.text |
2256 | 2257 |
# No meeting types yet |
2257 | 2258 |
assert 'Meeting Types' not in resp.text |
2259 |
# No absence yet |
|
2260 |
assert 'Excluded Periods' not in resp.text |
|
2258 | 2261 | |
2259 | 2262 | |
2260 | 2263 |
def test_virtual_agenda_settings(app, admin_user): |
... | ... | |
2265 | 2268 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2) |
2266 | 2269 |
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10) |
2267 | 2270 |
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10) |
2268 | ||
2271 |
TimePeriod.objects.create( |
|
2272 |
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0) |
|
2273 |
) |
|
2269 | 2274 |
app = login(app) |
2270 | 2275 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
2271 | 2276 |
assert "This virtual agenda doesn't include any agenda yet" not in resp.text |
... | ... | |
2278 | 2283 |
assert 'mt' in resp.text |
2279 | 2284 |
assert '10' in resp.text |
2280 | 2285 | |
2286 |
assert 'Excluded Periods' in resp.text |
|
2287 |
assert 'Monday' in resp.text |
|
2288 | ||
2281 | 2289 |
# Error message when incompatible meeting types |
2282 | 2290 |
mt2.delete() |
2283 | 2291 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
... | ... | |
2307 | 2315 |
assert len(resp.form['real_agenda'].options) == 2 |
2308 | 2316 | |
2309 | 2317 | |
2318 |
def test_virtual_agenda_settings_add_excluded_period(app, admin_user): |
|
2319 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2320 | ||
2321 |
app = login(app) |
|
2322 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2323 |
resp = resp.click('Add Excluded Period') |
|
2324 | ||
2325 |
resp.form['weekdays-0'].checked = True |
|
2326 |
resp.form['start_time'] = '10:00' |
|
2327 |
resp.form['end_time'] = '17:00' |
|
2328 |
resp = resp.form.submit() |
|
2329 |
tp = TimePeriod.objects.get(agenda=agenda) |
|
2330 |
assert tp.weekday == 0 |
|
2331 |
assert tp.start_time.hour == 10 |
|
2332 |
assert tp.start_time.minute == 0 |
|
2333 |
assert tp.end_time.hour == 17 |
|
2334 |
assert tp.end_time.minute == 0 |
|
2335 | ||
2336 |
resp = resp.follow() |
|
2337 |
assert u'Monday / 10 a.m. → 5 p.m.' in resp.text |
|
2338 | ||
2339 | ||
2340 |
def test_virtual_agenda_settings_edit_excluded_period(app, admin_user): |
|
2341 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2342 |
tp = TimePeriod.objects.create( |
|
2343 |
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0) |
|
2344 |
) |
|
2345 |
app = login(app) |
|
2346 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2347 |
url = '/manage/timeperiods/%s/edit' % tp.pk |
|
2348 |
resp = resp.click(href=url) |
|
2349 |
resp.form['start_time'] = '11:00' |
|
2350 |
resp = resp.form.submit() |
|
2351 |
tp = TimePeriod.objects.get(agenda=agenda) |
|
2352 |
assert tp.weekday == 0 |
|
2353 |
assert tp.start_time.hour == 11 |
|
2354 |
assert tp.start_time.minute == 0 |
|
2355 |
assert tp.end_time.hour == 18 |
|
2356 |
assert tp.end_time.minute == 0 |
|
2357 | ||
2358 | ||
2359 |
def test_virtual_agenda_settings_delete_excluded_period(app, admin_user): |
|
2360 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2361 |
tp = TimePeriod.objects.create( |
|
2362 |
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0) |
|
2363 |
) |
|
2364 |
app = login(app) |
|
2365 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2366 |
url = '/manage/timeperiods/%s/delete' % tp.pk |
|
2367 |
resp = resp.click(href=url) |
|
2368 |
resp = resp.form.submit() |
|
2369 |
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id) |
|
2370 |
assert TimePeriod.objects.count() == 0 |
|
2371 | ||
2372 | ||
2310 | 2373 |
def test_virtual_agenda_settings_include_incompatible_agenda(app, admin_user): |
2311 | 2374 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
2312 | 2375 |
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings') |
2313 |
- |