0002-manager-add-excluded-timeperiods-management-40058.patch
chrono/manager/forms.py | ||
---|---|---|
138 | 138 |
'start_time': widgets.TimeWidget(), |
139 | 139 |
'end_time': widgets.TimeWidget(), |
140 | 140 |
'desk': forms.HiddenInput(), |
141 |
'agenda': forms.HiddenInput(), |
|
141 | 142 |
} |
142 | 143 |
exclude = [] |
143 | 144 | |
145 |
def __init__(self, *args, **kwargs): |
|
146 |
has_desk = kwargs.pop('has_desk') |
|
147 |
super(TimePeriodForm, self).__init__(*args, **kwargs) |
|
148 |
if has_desk: |
|
149 |
del self.fields['agenda'] |
|
150 |
else: |
|
151 |
del self.fields['desk'] |
|
152 | ||
144 | 153 |
def clean_end_time(self): |
145 | 154 |
if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']: |
146 | 155 |
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 | ||
---|---|---|
644 | 644 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id}) |
645 | 645 | |
646 | 646 | |
647 |
class ManagedTimePeriodMixin(object): |
|
648 |
agenda = None |
|
649 | ||
650 |
def dispatch(self, request, *args, **kwargs): |
|
651 |
self.time_period = self.get_object() |
|
652 |
self.agenda = self.time_period.agenda |
|
653 |
self.has_desk = False |
|
654 |
if self.time_period.desk: |
|
655 |
self.agenda = self.time_period.desk.agenda |
|
656 |
self.has_desk = True |
|
657 | ||
658 |
if not self.agenda.can_be_managed(request.user): |
|
659 |
raise PermissionDenied() |
|
660 |
return super(ManagedTimePeriodMixin, self).dispatch(request, *args, **kwargs) |
|
661 | ||
662 |
def get_context_data(self, **kwargs): |
|
663 |
context = super(ManagedTimePeriodMixin, self).get_context_data(**kwargs) |
|
664 |
context['agenda'] = self.agenda |
|
665 |
return context |
|
666 | ||
667 |
def get_success_url(self): |
|
668 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) |
|
669 | ||
670 | ||
647 | 671 |
class AgendaSettings(ManagedAgendaMixin, DetailView): |
648 | 672 |
model = Agenda |
649 | 673 | |
... | ... | |
846 | 870 |
meeting_type_delete = MeetingTypeDeleteView.as_view() |
847 | 871 | |
848 | 872 | |
873 |
def process_time_period_add_form(form, desk=None, agenda=None): |
|
874 |
assert desk or agenda, "a time period requires a desk or a agenda" |
|
875 |
for weekday in form.cleaned_data.get('weekdays'): |
|
876 |
period = TimePeriod( |
|
877 |
weekday=weekday, |
|
878 |
start_time=form.cleaned_data['start_time'], |
|
879 |
end_time=form.cleaned_data['end_time'], |
|
880 |
) |
|
881 |
if desk: |
|
882 |
period.desk = desk |
|
883 |
elif agenda: |
|
884 |
period.agenda = agenda |
|
885 |
period.save() |
|
886 | ||
887 | ||
849 | 888 |
class AgendaAddTimePeriodView(ManagedDeskMixin, FormView): |
850 | 889 |
template_name = 'chrono/manager_time_period_form.html' |
851 | 890 |
form_class = TimePeriodAddForm |
852 | 891 | |
853 | 892 |
def form_valid(self, form): |
854 |
for weekday in form.cleaned_data.get('weekdays'): |
|
855 |
period = TimePeriod( |
|
856 |
weekday=weekday, |
|
857 |
start_time=form.cleaned_data['start_time'], |
|
858 |
end_time=form.cleaned_data['end_time'], |
|
859 |
desk=self.desk, |
|
860 |
) |
|
861 |
period.save() |
|
893 |
process_time_period_add_form(form, desk=self.desk) |
|
862 | 894 |
return super(AgendaAddTimePeriodView, self).form_valid(form) |
863 | 895 | |
864 | 896 | |
865 | 897 |
agenda_add_time_period = AgendaAddTimePeriodView.as_view() |
866 | 898 | |
867 | 899 | |
868 |
class TimePeriodEditView(ManagedDeskSubobjectMixin, UpdateView): |
|
900 |
class VirtualAgendaAddTimePeriodView(ManagedAgendaMixin, FormView): |
|
901 |
template_name = 'chrono/manager_time_period_form.html' |
|
902 |
form_class = TimePeriodAddForm |
|
903 | ||
904 |
def form_valid(self, form): |
|
905 |
process_time_period_add_form(form, agenda=self.agenda) |
|
906 |
return super(VirtualAgendaAddTimePeriodView, self).form_valid(form) |
|
907 | ||
908 | ||
909 |
virtual_agenda_add_time_period = VirtualAgendaAddTimePeriodView.as_view() |
|
910 | ||
911 | ||
912 |
class TimePeriodEditView(ManagedTimePeriodMixin, UpdateView): |
|
869 | 913 |
template_name = 'chrono/manager_time_period_form.html' |
870 | 914 |
model = TimePeriod |
871 | 915 |
form_class = TimePeriodForm |
872 | 916 | |
917 |
def get_form_kwargs(self): |
|
918 |
kwargs = super(TimePeriodEditView, self).get_form_kwargs() |
|
919 |
kwargs['has_desk'] = self.has_desk |
|
920 |
return kwargs |
|
921 | ||
873 | 922 | |
874 | 923 |
time_period_edit = TimePeriodEditView.as_view() |
875 | 924 | |
876 | 925 | |
877 |
class TimePeriodDeleteView(ManagedDeskSubobjectMixin, DeleteView):
|
|
926 |
class TimePeriodDeleteView(ManagedTimePeriodMixin, DeleteView):
|
|
878 | 927 |
template_name = 'chrono/manager_confirm_delete.html' |
879 | 928 |
model = TimePeriod |
880 | 929 |
tests/test_manager.py | ||
---|---|---|
2195 | 2195 |
assert 'Export' in resp.text |
2196 | 2196 |
assert 'Delete' in resp.text |
2197 | 2197 |
assert 'Included Agendas' in resp.text |
2198 |
assert 'Add Excluded Period' in resp.text |
|
2198 | 2199 |
assert "This virtual agenda doesn't include any agenda yet" in resp.text |
2199 | 2200 |
# No meeting types yet |
2200 | 2201 |
assert 'Meeting Types' not in resp.text |
2202 |
# No absence yet |
|
2203 |
assert 'Excluded Periods' not in resp.text |
|
2201 | 2204 | |
2202 | 2205 | |
2203 | 2206 |
def test_virtual_agenda_settings(app, admin_user): |
... | ... | |
2208 | 2211 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2) |
2209 | 2212 |
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10) |
2210 | 2213 |
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10) |
2211 | ||
2214 |
TimePeriod.objects.create( |
|
2215 |
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0) |
|
2216 |
) |
|
2212 | 2217 |
app = login(app) |
2213 | 2218 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
2214 | 2219 |
assert "This virtual agenda doesn't include any agenda yet" not in resp.text |
... | ... | |
2221 | 2226 |
assert 'mt' in resp.text |
2222 | 2227 |
assert '10' in resp.text |
2223 | 2228 | |
2229 |
assert 'Excluded Periods' in resp.text |
|
2230 |
assert 'Monday' in resp.text |
|
2231 | ||
2224 | 2232 |
# Error message when incompatible meeting types |
2225 | 2233 |
mt2.delete() |
2226 | 2234 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
... | ... | |
2250 | 2258 |
assert len(resp.form['real_agenda'].options) == 2 |
2251 | 2259 | |
2252 | 2260 | |
2261 |
def test_virtual_agenda_settings_add_excluded_period(app, admin_user): |
|
2262 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2263 | ||
2264 |
app = login(app) |
|
2265 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2266 |
resp = resp.click('Add Excluded Period') |
|
2267 | ||
2268 |
resp.form['weekdays-0'].checked = True |
|
2269 |
resp.form['start_time'] = '10:00' |
|
2270 |
resp.form['end_time'] = '17:00' |
|
2271 |
resp = resp.form.submit() |
|
2272 |
tp = TimePeriod.objects.get(agenda=agenda) |
|
2273 |
assert tp.weekday == 0 |
|
2274 |
assert tp.start_time.hour == 10 |
|
2275 |
assert tp.start_time.minute == 0 |
|
2276 |
assert tp.end_time.hour == 17 |
|
2277 |
assert tp.end_time.minute == 0 |
|
2278 | ||
2279 |
resp = resp.follow() |
|
2280 |
assert u'Monday / 10 a.m. → 5 p.m.' in resp.text |
|
2281 | ||
2282 | ||
2283 |
def test_virtual_agenda_settings_edit_excluded_period(app, admin_user): |
|
2284 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2285 |
tp = TimePeriod.objects.create( |
|
2286 |
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0) |
|
2287 |
) |
|
2288 |
app = login(app) |
|
2289 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2290 |
url = '/manage/timeperiods/%s/edit' % tp.pk |
|
2291 |
resp = resp.click(href=url) |
|
2292 |
resp.form['start_time'] = '11:00' |
|
2293 |
resp = resp.form.submit() |
|
2294 |
tp = TimePeriod.objects.get(agenda=agenda) |
|
2295 |
assert tp.weekday == 0 |
|
2296 |
assert tp.start_time.hour == 11 |
|
2297 |
assert tp.start_time.minute == 0 |
|
2298 |
assert tp.end_time.hour == 18 |
|
2299 |
assert tp.end_time.minute == 0 |
|
2300 | ||
2301 | ||
2302 |
def test_virtual_agenda_settings_delete_excluded_period(app, admin_user): |
|
2303 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2304 |
tp = TimePeriod.objects.create( |
|
2305 |
agenda=agenda, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0) |
|
2306 |
) |
|
2307 |
app = login(app) |
|
2308 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2309 |
url = '/manage/timeperiods/%s/delete' % tp.pk |
|
2310 |
resp = resp.click(href=url) |
|
2311 |
resp = resp.form.submit() |
|
2312 |
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id) |
|
2313 |
assert TimePeriod.objects.count() == 0 |
|
2314 | ||
2315 | ||
2253 | 2316 |
def test_virtual_agenda_settings_include_incompatible_agenda(app, admin_user): |
2254 | 2317 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
2255 | 2318 |
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings') |
2256 |
- |