0001-start-unavailability-calendars-46555.patch
chrono/agendas/migrations/0065_unavailability_calendar.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.18 on 2020-09-30 11:52 |
|
3 |
from __future__ import unicode_literals |
|
4 | ||
5 |
from django.db import migrations, models |
|
6 |
import django.db.models.deletion |
|
7 | ||
8 | ||
9 |
class Migration(migrations.Migration): |
|
10 | ||
11 |
dependencies = [ |
|
12 |
('auth', '0008_alter_user_username_max_length'), |
|
13 |
('agendas', '0064_booking_form_url'), |
|
14 |
] |
|
15 | ||
16 |
operations = [ |
|
17 |
migrations.CreateModel( |
|
18 |
name='UnavailabilityCalendar', |
|
19 |
fields=[ |
|
20 |
( |
|
21 |
'id', |
|
22 |
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), |
|
23 |
), |
|
24 |
('label', models.CharField(max_length=150, verbose_name='Label')), |
|
25 |
('slug', models.SlugField(max_length=160, unique=True, verbose_name='Identifier')), |
|
26 |
( |
|
27 |
'agendas', |
|
28 |
models.ManyToManyField(related_name='unavailability_calendars', to='agendas.Agenda'), |
|
29 |
), |
|
30 |
( |
|
31 |
'edit_role', |
|
32 |
models.ForeignKey( |
|
33 |
blank=True, |
|
34 |
default=None, |
|
35 |
null=True, |
|
36 |
on_delete=django.db.models.deletion.SET_NULL, |
|
37 |
related_name='+', |
|
38 |
to='auth.Group', |
|
39 |
verbose_name='Edit Role', |
|
40 |
), |
|
41 |
), |
|
42 |
( |
|
43 |
'view_role', |
|
44 |
models.ForeignKey( |
|
45 |
blank=True, |
|
46 |
default=None, |
|
47 |
null=True, |
|
48 |
on_delete=django.db.models.deletion.SET_NULL, |
|
49 |
related_name='+', |
|
50 |
to='auth.Group', |
|
51 |
verbose_name='View Role', |
|
52 |
), |
|
53 |
), |
|
54 |
], |
|
55 |
options={'ordering': ['label'],}, |
|
56 |
), |
|
57 |
migrations.AlterField( |
|
58 |
model_name='timeperiodexception', |
|
59 |
name='desk', |
|
60 |
field=models.ForeignKey( |
|
61 |
null=True, on_delete=django.db.models.deletion.CASCADE, to='agendas.Desk' |
|
62 |
), |
|
63 |
), |
|
64 |
migrations.AddField( |
|
65 |
model_name='timeperiodexception', |
|
66 |
name='unavailability_calendar', |
|
67 |
field=models.ForeignKey( |
|
68 |
null=True, on_delete=django.db.models.deletion.CASCADE, to='agendas.UnavailabilityCalendar' |
|
69 |
), |
|
70 |
), |
|
71 |
] |
chrono/agendas/models.py | ||
---|---|---|
1486 | 1486 |
self.save() |
1487 | 1487 | |
1488 | 1488 | |
1489 |
class UnavailabilityCalendar(models.Model): |
|
1490 |
label = models.CharField(_('Label'), max_length=150) |
|
1491 |
slug = models.SlugField(_('Identifier'), max_length=160, unique=True) |
|
1492 |
agendas = models.ManyToManyField(Agenda, related_name='unavailability_calendars') |
|
1493 |
edit_role = models.ForeignKey( |
|
1494 |
Group, |
|
1495 |
blank=True, |
|
1496 |
null=True, |
|
1497 |
default=None, |
|
1498 |
related_name='+', |
|
1499 |
verbose_name=_('Edit Role'), |
|
1500 |
on_delete=models.SET_NULL, |
|
1501 |
) |
|
1502 |
view_role = models.ForeignKey( |
|
1503 |
Group, |
|
1504 |
blank=True, |
|
1505 |
null=True, |
|
1506 |
default=None, |
|
1507 |
related_name='+', |
|
1508 |
verbose_name=_('View Role'), |
|
1509 |
on_delete=models.SET_NULL, |
|
1510 |
) |
|
1511 | ||
1512 |
class Meta: |
|
1513 |
ordering = ['label'] |
|
1514 | ||
1515 |
def __str__(self): |
|
1516 |
return self.label |
|
1517 | ||
1518 |
@property |
|
1519 |
def base_slug(self): |
|
1520 |
return slugify(self.label) |
|
1521 | ||
1522 |
def save(self, *args, **kwargs): |
|
1523 |
if not self.slug: |
|
1524 |
self.slug = generate_slug(self) |
|
1525 |
super(UnavailabilityCalendar, self).save(*args, **kwargs) |
|
1526 | ||
1527 |
def can_be_managed(self, user): |
|
1528 |
if user.is_staff: |
|
1529 |
return True |
|
1530 |
group_ids = [x.id for x in user.groups.all()] |
|
1531 |
return bool(self.edit_role_id in group_ids) |
|
1532 | ||
1533 |
def can_be_viewed(self, user): |
|
1534 |
if self.can_be_managed(user): |
|
1535 |
return True |
|
1536 |
group_ids = [x.id for x in user.groups.all()] |
|
1537 |
return bool(self.view_role_id in group_ids) |
|
1538 | ||
1539 |
def get_absolute_url(self): |
|
1540 |
return reverse('chrono-manager-unavailability-calendar-view', kwargs={'pk': self.id}) |
|
1541 | ||
1542 | ||
1489 | 1543 |
class TimePeriodException(models.Model): |
1490 |
desk = models.ForeignKey(Desk, on_delete=models.CASCADE) |
|
1544 |
desk = models.ForeignKey(Desk, on_delete=models.CASCADE, null=True) |
|
1545 |
unavailability_calendar = models.ForeignKey(UnavailabilityCalendar, on_delete=models.CASCADE, null=True) |
|
1491 | 1546 |
source = models.ForeignKey(TimePeriodExceptionSource, on_delete=models.CASCADE, null=True) |
1492 | 1547 |
label = models.CharField(_('Optional Label'), max_length=150, blank=True, null=True) |
1493 | 1548 |
start_datetime = models.DateTimeField(_('Exception start time')) |
chrono/api/views.py | ||
---|---|---|
141 | 141 |
key=lambda time_period: time_period.desk, |
142 | 142 |
) |
143 | 143 |
} |
144 | ||
145 |
# add exceptions from unavailability calendar |
|
146 |
for time_period_exception in TimePeriodException.objects.filter( |
|
147 |
unavailability_calendar__agendas__in=agendas |
|
148 |
).order_by('start_datetime', 'end_datetime'): |
|
149 |
for agenda in time_period_exception.unavailability_calendar.agendas.all(): |
|
150 |
for desk in agenda.desk_set.all(): |
|
151 |
if desk not in desks_exceptions: |
|
152 |
desks_exceptions[desk] = IntervalSet() |
|
153 |
desks_exceptions[desk].add( |
|
154 |
time_period_exception.start_datetime, time_period_exception.end_datetime |
|
155 |
) |
|
156 | ||
144 | 157 |
# compute reduced min/max_datetime windows by desks based on exceptions |
145 | 158 |
desk_min_max_datetime = {} |
146 | 159 |
for desk, desk_exception in desks_exceptions.items(): |
chrono/manager/forms.py | ||
---|---|---|
44 | 44 |
AgendaNotificationsSettings, |
45 | 45 |
AgendaReminderSettings, |
46 | 46 |
WEEKDAYS_LIST, |
47 |
UnavailabilityCalendar, |
|
47 | 48 |
) |
48 | 49 | |
49 | 50 |
from . import widgets |
... | ... | |
89 | 90 |
del self.fields['booking_form_url'] |
90 | 91 | |
91 | 92 | |
93 |
class UnavailabilityCalendarAddForm(forms.ModelForm): |
|
94 |
class Meta: |
|
95 |
model = UnavailabilityCalendar |
|
96 |
fields = ['label', 'edit_role', 'view_role'] |
|
97 | ||
98 |
edit_role = forms.ModelChoiceField( |
|
99 |
label=_('Edit Role'), required=False, queryset=Group.objects.all().order_by('name') |
|
100 |
) |
|
101 |
view_role = forms.ModelChoiceField( |
|
102 |
label=_('View Role'), required=False, queryset=Group.objects.all().order_by('name') |
|
103 |
) |
|
104 | ||
105 | ||
106 |
class UnavailabilityCalendarEditForm(UnavailabilityCalendarAddForm): |
|
107 |
class Meta: |
|
108 |
model = UnavailabilityCalendar |
|
109 |
fields = ['label', 'slug', 'edit_role', 'view_role'] |
|
110 | ||
111 | ||
92 | 112 |
class ResourceAddForm(forms.ModelForm): |
93 | 113 |
class Meta: |
94 | 114 |
model = Resource |
... | ... | |
170 | 190 |
self.fields['resource'].queryset = Resource.objects.exclude(agenda=self.initial['agenda']) |
171 | 191 | |
172 | 192 | |
193 |
class AgendaUnavailabilityCalendarForm(forms.Form): |
|
194 |
unavailability_calendar = forms.ModelChoiceField( |
|
195 |
label=_('UnavailabilityCalendar'), queryset=UnavailabilityCalendar.objects.none() |
|
196 |
) |
|
197 | ||
198 |
def __init__(self, *args, **kwargs): |
|
199 |
super().__init__(*args, **kwargs) |
|
200 |
self.fields['unavailability_calendar'].queryset = UnavailabilityCalendar.objects.exclude( |
|
201 |
agendas=self.initial['agenda'] |
|
202 |
) |
|
203 | ||
204 | ||
173 | 205 |
class NewMeetingTypeForm(forms.ModelForm): |
174 | 206 |
class Meta: |
175 | 207 |
model = MeetingType |
... | ... | |
287 | 319 | |
288 | 320 |
class Meta: |
289 | 321 |
model = TimePeriodException |
290 |
fields = ['desk', 'start_datetime', 'end_datetime', 'label'] |
|
322 |
fields = ['desk', 'start_datetime', 'end_datetime', 'label', 'unavailability_calendar']
|
|
291 | 323 |
widgets = { |
292 | 324 |
'desk': forms.HiddenInput(), |
325 |
'unavailability_calendar': forms.HiddenInput(), |
|
293 | 326 |
} |
294 | 327 |
field_classes = { |
295 | 328 |
'start_datetime': SplitDateTimeField, |
... | ... | |
297 | 330 |
} |
298 | 331 | |
299 | 332 |
def __init__(self, *args, **kwargs): |
333 |
has_desk = kwargs.pop('has_desk', True) |
|
300 | 334 |
super().__init__(*args, **kwargs) |
301 |
if self.instance.pk is not None: |
|
302 |
del self.fields['all_desks'] |
|
303 |
elif 'desk' in self.initial and self.initial['desk'].agenda.desk_set.count() == 1: |
|
335 |
if has_desk: |
|
336 |
del self.fields['unavailability_calendar'] |
|
337 |
if self.instance.pk is not None or ( |
|
338 |
'desk' in self.initial and self.initial['desk'].agenda.desk_set.count() == 1 |
|
339 |
): |
|
340 |
del self.fields['all_desks'] |
|
341 |
else: |
|
342 |
del self.fields['desk'] |
|
304 | 343 |
del self.fields['all_desks'] |
305 | 344 | |
306 | 345 |
def clean(self): |
chrono/manager/templates/chrono/manager_agenda_unavailability_calendar_form.html | ||
---|---|---|
1 |
{% extends "chrono/manager_agenda_view.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block breadcrumb %} |
|
5 |
{{ block.super }} |
|
6 |
<a href="">{% trans "Add unavailability calendar" %}</a> |
|
7 |
{% endblock %} |
|
8 | ||
9 |
{% block appbar %} |
|
10 |
<h2>{% trans "Add unavailability calendar" %}</h2> |
|
11 |
{% endblock %} |
|
12 | ||
13 |
{% block content %} |
|
14 | ||
15 |
<form method="post" enctype="multipart/form-data"> |
|
16 |
{% csrf_token %} |
|
17 |
{{ form.as_p }} |
|
18 |
<div class="buttons"> |
|
19 |
<button class="submit-button">{% trans "Save" %}</button> |
|
20 |
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.pk %}">{% trans 'Cancel' %}</a> |
|
21 |
</div> |
|
22 |
</form> |
|
23 |
{% endblock %} |
chrono/manager/templates/chrono/manager_home.html | ||
---|---|---|
3 | 3 | |
4 | 4 |
{% block appbar %} |
5 | 5 |
<h2>{% trans 'Agendas' %}</h2> |
6 |
{% if user.is_staff %} |
|
7 | 6 |
<span class="actions"> |
7 |
{% if user.is_staff %} |
|
8 | 8 |
<a href="{% url 'chrono-manager-category-list' %}">{% trans 'Categories' %}</a> |
9 |
{% endif %} |
|
10 |
<a href="{% url 'chrono-manager-unavailability-calendar-list' %}">{% trans 'Unavailability calendars' %}</a> |
|
11 |
{% if user.is_staff %} |
|
9 | 12 |
<a href="{% url 'chrono-manager-resource-list' %}">{% trans 'Resources' %}</a> |
10 | 13 |
<a rel="popup" href="{% url 'chrono-manager-agendas-import' %}">{% trans 'Import' %}</a> |
11 | 14 |
<a rel="popup" href="{% url 'chrono-manager-agenda-add' %}">{% trans 'New' %}</a> |
12 |
</span> |
|
13 | 15 |
{% endif %} |
16 |
</span> |
|
14 | 17 |
{% endblock %} |
15 | 18 | |
16 | 19 |
{% block content %} |
chrono/manager/templates/chrono/manager_meetings_agenda_settings.html | ||
---|---|---|
14 | 14 | |
15 | 15 |
{% block agenda-extra-management-actions %} |
16 | 16 |
{% if has_resources %}<a rel="popup" href="{% url 'chrono-manager-agenda-add-resource' pk=object.pk %}">{% trans 'Add resource' %}</a>{% endif %} |
17 |
{% if has_unavailability_calendars %}<a rel="popup" href="{% url 'chrono-manager-agenda-add-unavailability-calendar' pk=object.pk %}">{% trans 'Add unavailability calendar' %}</a>{% endif %} |
|
17 | 18 |
<a rel="popup" href="{% url 'chrono-manager-agenda-add-meeting-type' pk=object.id %}">{% trans 'New Meeting Type' %}</a> |
18 | 19 |
<a rel="popup" href="{% url 'chrono-manager-agenda-add-desk' pk=object.id %}">{% trans 'New Desk' %}</a> |
19 | 20 |
{% endblock %} |
... | ... | |
98 | 99 |
</div> |
99 | 100 |
</div> |
100 | 101 | |
102 |
{% with object.unavailability_calendars.all as agenda_unavailability_calendars %} |
|
103 |
{% if has_unavailability_calendars %} |
|
104 |
<div class="section"> |
|
105 |
<h3>{% trans 'Unavailability calendars' %}</h3> |
|
106 |
<div> |
|
107 |
{% if agenda_unavailability_calendars %} |
|
108 |
<ul class="objects-list single-links"> |
|
109 |
{% for unavailability_calendar in agenda_unavailability_calendars %} |
|
110 |
<li> |
|
111 |
<a href="{% url 'chrono-manager-unavailability-calendar-view' pk=unavailability_calendar.pk %}"> |
|
112 |
{{ unavailability_calendar.label }} |
|
113 |
<span class="identifier">[{% trans "identifier:" %} {{ unavailability_calendar.slug }}]</span> |
|
114 |
</a> |
|
115 |
<a rel="popup" class="delete" href="{% url 'chrono-manager-agenda-delete-unavailability_calendar' pk=object.pk unavailability_calendar_pk=unavailability_calendar.pk %}">{% trans "remove" %}</a> |
|
116 |
</li> |
|
117 |
{% endfor %} |
|
118 |
</ul> |
|
119 |
{% else %} |
|
120 |
<div class="big-msg-info"> |
|
121 |
{% blocktrans %} |
|
122 |
This agenda doesn't have any unavailability calendar yet. Click on the "Add unavailability calendar" button in |
|
123 |
the top right of the page to add a first one. |
|
124 |
{% endblocktrans %} |
|
125 |
</div> |
|
126 |
{% endif %} |
|
127 |
</div> |
|
128 |
</div> |
|
129 |
{% endif %} |
|
130 |
{% endwith %} |
|
131 | ||
132 | ||
101 | 133 |
{% with object.resources.all as agenda_resources %} |
102 | 134 |
{% if has_resources %} |
103 | 135 |
<div class="section"> |
chrono/manager/templates/chrono/manager_time_period_exception_form.html | ||
---|---|---|
40 | 40 |
{% endfor %} |
41 | 41 |
<div class="buttons"> |
42 | 42 |
<button class="submit-button">{% trans "Save" %}</button> |
43 |
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=desk.agenda.id %}">{% trans 'Cancel' %}</a>
|
|
43 |
<a class="cancel" href="{{ cancel_url }}">{% trans 'Cancel' %}</a>
|
|
44 | 44 |
</div> |
45 | 45 | |
46 | 46 |
<script> |
chrono/manager/templates/chrono/manager_unavailability_calendar_detail.html | ||
---|---|---|
1 |
{% extends "chrono/manager_unavailability_calendar_list.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block page-title-extra-label %} |
|
5 |
- {{ calendar.label }} |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% block breadcrumb %} |
|
9 |
{{ block.super }} |
|
10 |
<a href="{% url 'chrono-manager-unavailability-calendar-view' pk=unavailability_calendar.pk %}">{{ unavailability_calendar.label }}</a> |
|
11 |
{% endblock %} |
|
12 | ||
13 |
{% block appbar %} |
|
14 |
{% block appbar-title %} |
|
15 |
<h2>{{ unavailability_calendar }}</h2> |
|
16 |
{% endblock %} |
|
17 |
{% if user_can_manage %} |
|
18 |
<span class="actions"> |
|
19 |
{% block appbar-extras %} |
|
20 |
<a href="{% url 'chrono-manager-unavailability-calendar-settings' pk=unavailability_calendar.pk %}">{% trans 'Settings' %}</a> |
|
21 |
{% endblock %} |
|
22 |
</span> |
|
23 |
{% endif %} |
|
24 |
{% endblock %} |
|
25 | ||
26 |
{% block content %} |
|
27 | ||
28 |
<div class="section"> |
|
29 |
<h3>{% trans 'Used in meetings agendas' %}</h3> |
|
30 |
<div> |
|
31 |
{% with unavailability_calendar.agendas.all as agendas %} |
|
32 |
{% if agendas %} |
|
33 |
<ul class="objects-list single-links"> |
|
34 |
{% for agenda in agendas %} |
|
35 |
<li> |
|
36 |
<a href="{% url 'chrono-manager-agenda-view' pk=agenda.pk %}"> |
|
37 |
{{ agenda.label }} |
|
38 |
</a> |
|
39 |
</li> |
|
40 |
{% endfor %} |
|
41 |
</ul> |
|
42 |
{% else %} |
|
43 |
<div class="big-msg-info"> |
|
44 |
{% blocktrans %} |
|
45 |
This unavailability calendar is not used yet. |
|
46 |
{% endblocktrans %} |
|
47 |
</div> |
|
48 |
{% endif %} |
|
49 |
{% endwith %} |
|
50 |
</div> |
|
51 |
</div> |
|
52 | ||
53 |
{% endblock %} |
chrono/manager/templates/chrono/manager_unavailability_calendar_form.html | ||
---|---|---|
1 |
{% extends "chrono/manager_home.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block appbar %} |
|
5 |
{% if object.id %} |
|
6 |
<h2>{% trans "Edit Unavailability Calendar" %}</h2> |
|
7 |
{% else %} |
|
8 |
<h2>{% trans "New Unavailability Calendar" %}</h2> |
|
9 |
{% endif %} |
|
10 |
{% endblock %} |
|
11 | ||
12 |
{% block content %} |
|
13 | ||
14 |
<form method="post" enctype="multipart/form-data"> |
|
15 |
{% csrf_token %} |
|
16 |
{{ form.as_p }} |
|
17 |
<div class="buttons"> |
|
18 |
<button class="submit-button">{% trans "Save" %}</button> |
|
19 |
<a class="cancel" href="{% url 'chrono-manager-unavailability-calendar-list' %}">{% trans 'Cancel' %}</a> |
|
20 |
</div> |
|
21 |
</form> |
|
22 |
{% endblock %} |
chrono/manager/templates/chrono/manager_unavailability_calendar_list.html | ||
---|---|---|
1 |
{% extends "chrono/manager_base.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block breadcrumb %} |
|
5 |
{{ block.super }} |
|
6 |
<a href="{% url 'chrono-manager-unavailability-calendar-list' %}">{% trans "Unavailability Calendars" %}</a> |
|
7 |
{% endblock %} |
|
8 | ||
9 |
{% block appbar %} |
|
10 |
<h2>{% trans 'Unavailability Calendars' %}</h2> |
|
11 |
{% if user.is_staff %} |
|
12 |
<span class="actions"> |
|
13 |
<a rel="popup" href="{% url 'chrono-manager-unavailability-calendar-add' %}">{% trans 'New' %}</a> |
|
14 |
</span> |
|
15 |
{% endif %} |
|
16 |
{% endblock %} |
|
17 | ||
18 | ||
19 |
{% block content %} |
|
20 |
{% if object_list %} |
|
21 |
<div> |
|
22 |
<ul class="objects-list single-links"> |
|
23 |
{% for object in object_list %} |
|
24 |
<li> |
|
25 |
<a href="{% url 'chrono-manager-unavailability-calendar-view' pk=object.pk %}">{{ object.label }} ({{ object.slug }})</a> |
|
26 |
</li> |
|
27 |
{% endfor %} |
|
28 |
</ul> |
|
29 |
</div> |
|
30 |
{% else %} |
|
31 |
<div class="big-msg-info"> |
|
32 |
{% blocktrans %} |
|
33 |
This site doesn't have any unavailability calendar yet. Click on the "New" button in the top |
|
34 |
right of the page to add a first one. |
|
35 |
{% endblocktrans %} |
|
36 |
</div> |
|
37 |
{% endif %} |
|
38 |
{% endblock %} |
chrono/manager/templates/chrono/manager_unavailability_calendar_settings.html | ||
---|---|---|
1 |
{% extends "chrono/manager_unavailability_calendar_detail.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block breadcrumb %} |
|
5 |
{{ block.super }} |
|
6 |
<a href=".">{% trans "Settings" %}</a> |
|
7 |
{% endblock %} |
|
8 | ||
9 |
{% block appbar %} |
|
10 |
<h2>{% trans "Settings" %} |
|
11 |
<span class="identifier">[{% trans "identifier:" %} {{unavailability_calendar.slug}}]</span> |
|
12 |
</h2> |
|
13 |
<span class="actions"> |
|
14 |
<a class="extra-actions-menu-opener"></a> |
|
15 |
{% block agenda-extra-management-actions %} |
|
16 |
<a rel="popup" href="{% url 'chrono-manager-unavailability-calendar-add-unavailability' pk=unavailability_calendar.id %}">{% trans 'Add Unavailability' %}</a> |
|
17 |
{% endblock %} |
|
18 |
<ul class="extra-actions-menu"> |
|
19 |
<li><a rel="popup" href="{% url 'chrono-manager-unavailability-calendar-edit' pk=unavailability_calendar.id %}">{% trans 'Options' %}</a></li> |
|
20 |
{% if user.is_staff %} |
|
21 |
<li><a rel="popup" href="{% url 'chrono-manager-unavailability-calendar-delete' pk=unavailability_calendar.id %}">{% trans 'Delete' %}</a></li> |
|
22 |
{% endif %} |
|
23 |
</ul> |
|
24 |
</span> |
|
25 |
{% endblock %} |
|
26 | ||
27 |
{% block content %} |
|
28 |
<div class="section"> |
|
29 |
<h3>{% trans 'Unavailabilities' %}</h3> |
|
30 |
<div> |
|
31 |
{% block agenda-settings %} |
|
32 |
{% if unavailability_calendar.timeperiodexception_set.count %} |
|
33 |
<ul class="objects-list single-links"> |
|
34 |
{% for time_period_exception in unavailability_calendar.timeperiodexception_set.all %} |
|
35 |
<li><a rel="popup" href="{% url 'chrono-manager-time-period-exception-edit' pk=time_period_exception.id %}">{{ time_period_exception }}</a> |
|
36 |
<a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-exception-delete' pk=time_period_exception.id %}">{% trans "remove" %}</a> |
|
37 |
</li> |
|
38 |
{% endfor %} |
|
39 |
</ul> |
|
40 |
{% else %} |
|
41 |
<div class="big-msg-info"> |
|
42 |
{% blocktrans %} |
|
43 |
There is no unavailabilities yet. Click on the "Add Unavailabilty" button in |
|
44 |
the top right of the page to add a first one. |
|
45 |
{% endblocktrans %} |
|
46 |
</div> |
|
47 |
{% endif %} |
|
48 |
</div> |
|
49 |
</div> |
|
50 |
{% endblock %} |
|
51 | ||
52 | ||
53 |
{% endblock %} |
chrono/manager/urls.py | ||
---|---|---|
20 | 20 | |
21 | 21 |
urlpatterns = [ |
22 | 22 |
url(r'^$', views.homepage, name='chrono-manager-homepage'), |
23 |
url( |
|
24 |
r'^unavailability-calendars/$', |
|
25 |
views.unavailability_calendar_list, |
|
26 |
name='chrono-manager-unavailability-calendar-list', |
|
27 |
), |
|
28 |
url( |
|
29 |
r'^unavailability-calendar/add/$', |
|
30 |
views.unavailability_calendar_add, |
|
31 |
name='chrono-manager-unavailability-calendar-add', |
|
32 |
), |
|
33 |
url( |
|
34 |
r'^unavailability-calendar/(?P<pk>\d+)/$', |
|
35 |
views.unavailability_calendar_view, |
|
36 |
name='chrono-manager-unavailability-calendar-view', |
|
37 |
), |
|
38 |
url( |
|
39 |
r'^unavailability-calendar/(?P<pk>\d+)/edit/$', |
|
40 |
views.unavailability_calendar_edit, |
|
41 |
name='chrono-manager-unavailability-calendar-edit', |
|
42 |
), |
|
43 |
url( |
|
44 |
r'^unavailability-calendar/(?P<pk>\d+)/delete/$', |
|
45 |
views.unavailability_calendar_delete, |
|
46 |
name='chrono-manager-unavailability-calendar-delete', |
|
47 |
), |
|
48 |
url( |
|
49 |
r'^unavailability-calendar/(?P<pk>\d+)/settings$', |
|
50 |
views.unavailability_calendar_settings, |
|
51 |
name='chrono-manager-unavailability-calendar-settings', |
|
52 |
), |
|
53 |
url( |
|
54 |
r'^unavailability-calendar/(?P<pk>\d+)/add-unavailability$', |
|
55 |
views.unavailability_calendar_add_unavailability, |
|
56 |
name='chrono-manager-unavailability-calendar-add-unavailability', |
|
57 |
), |
|
23 | 58 |
url(r'^resources/$', views.resource_list, name='chrono-manager-resource-list'), |
24 | 59 |
url(r'^resource/add/$', views.resource_add, name='chrono-manager-resource-add'), |
25 | 60 |
url(r'^resource/(?P<pk>\d+)/$', views.resource_view, name='chrono-manager-resource-view'), |
... | ... | |
149 | 184 |
views.virtual_agenda_add_time_period, |
150 | 185 |
name='chrono-manager-virtual-agenda-add-time-period', |
151 | 186 |
), |
187 |
url( |
|
188 |
r'^agendas/(?P<pk>\d+)/add-unavailability-calendar$', |
|
189 |
views.agenda_add_unavailability_calendar, |
|
190 |
name='chrono-manager-agenda-add-unavailability-calendar', |
|
191 |
), |
|
192 |
url( |
|
193 |
r'^agendas/(?P<pk>\d+)/unavailability-calendar/(?P<unavailability_calendar_pk>\d+)/delete/$', |
|
194 |
views.agenda_delete_unavailability_calendar, |
|
195 |
name='chrono-manager-agenda-delete-unavailability_calendar', |
|
196 |
), |
|
152 | 197 |
url(r'^timeperiods/(?P<pk>\d+)/edit$', views.time_period_edit, name='chrono-manager-time-period-edit'), |
153 | 198 |
url( |
154 | 199 |
r'^timeperiods/(?P<pk>\d+)/delete$', |
chrono/manager/views.py | ||
---|---|---|
65 | 65 |
EventCancellationReport, |
66 | 66 |
AgendaNotificationsSettings, |
67 | 67 |
AgendaReminderSettings, |
68 |
UnavailabilityCalendar, |
|
68 | 69 |
) |
69 | 70 | |
70 | 71 |
from .forms import ( |
... | ... | |
94 | 95 |
EventCancelForm, |
95 | 96 |
AgendaNotificationsForm, |
96 | 97 |
AgendaReminderForm, |
98 |
UnavailabilityCalendarAddForm, |
|
99 |
UnavailabilityCalendarEditForm, |
|
100 |
AgendaUnavailabilityCalendarForm, |
|
97 | 101 |
) |
98 | 102 |
from .utils import import_site |
99 | 103 | |
... | ... | |
1235 | 1239 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) |
1236 | 1240 | |
1237 | 1241 | |
1242 |
class ManagedTimePeriodExceptionMixin(object): |
|
1243 | ||
1244 |
desk = None |
|
1245 |
unavailability_calendar = None |
|
1246 | ||
1247 |
def dispatch(self, request, *args, **kwargs): |
|
1248 |
object_ = self.get_object() |
|
1249 |
if object_.desk: |
|
1250 |
self.desk = self.get_object().desk |
|
1251 |
if not self.desk.agenda.can_be_managed(request.user): |
|
1252 |
raise PermissionDenied() |
|
1253 |
elif object_.unavailability_calendar: |
|
1254 |
self.unavailability_calendar = object_.unavailability_calendar |
|
1255 |
if not self.unavailability_calendar.can_be_managed(request.user): |
|
1256 |
raise PermissionDenied() |
|
1257 |
return super().dispatch(request, *args, **kwargs) |
|
1258 | ||
1259 |
def get_context_data(self, **kwargs): |
|
1260 |
context = super().get_context_data(**kwargs) |
|
1261 |
if self.desk: |
|
1262 |
context['desk'] = self.object.desk |
|
1263 |
context['agenda'] = self.object.desk.agenda |
|
1264 |
return context |
|
1265 | ||
1266 |
def get_form_kwargs(self): |
|
1267 |
kwargs = super().get_form_kwargs() |
|
1268 |
kwargs['has_desk'] = True |
|
1269 |
if self.unavailability_calendar: |
|
1270 |
kwargs['has_desk'] = False |
|
1271 |
return kwargs |
|
1272 | ||
1273 |
def get_success_url(self): |
|
1274 |
if self.desk: |
|
1275 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id}) |
|
1276 |
elif self.unavailability_calendar: |
|
1277 |
return reverse( |
|
1278 |
'chrono-manager-unavailability-calendar-settings', |
|
1279 |
kwargs={'pk': self.unavailability_calendar.pk}, |
|
1280 |
) |
|
1281 | ||
1282 | ||
1238 | 1283 |
class AgendaSettings(ManagedAgendaMixin, DetailView): |
1239 | 1284 |
model = Agenda |
1240 | 1285 | |
... | ... | |
1256 | 1301 |
] |
1257 | 1302 |
if self.agenda.kind == 'meetings': |
1258 | 1303 |
context['has_resources'] = Resource.objects.exists() |
1304 |
context['has_unavailability_calendars'] = UnavailabilityCalendar.objects.exists() |
|
1259 | 1305 |
return context |
1260 | 1306 | |
1261 | 1307 |
def get_events(self): |
... | ... | |
1530 | 1576 |
agenda_delete_resource = AgendaResourceDeleteView.as_view() |
1531 | 1577 | |
1532 | 1578 | |
1579 |
class AgendaAddUnavailabilityCalendarView(ManagedAgendaMixin, FormView): |
|
1580 |
template_name = 'chrono/manager_agenda_unavailability_calendar_form.html' |
|
1581 |
form_class = AgendaUnavailabilityCalendarForm |
|
1582 | ||
1583 |
def set_agenda(self, **kwargs): |
|
1584 |
self.agenda = get_object_or_404(Agenda, id=kwargs.get('pk'), kind='meetings') |
|
1585 | ||
1586 |
def form_valid(self, form): |
|
1587 |
self.agenda.unavailability_calendars.add(form.cleaned_data['unavailability_calendar']) |
|
1588 |
return super().form_valid(form) |
|
1589 | ||
1590 | ||
1591 |
agenda_add_unavailability_calendar = AgendaAddUnavailabilityCalendarView.as_view() |
|
1592 | ||
1593 | ||
1594 |
class AgendaUnavailabilityCalendarDeleteView(ManagedAgendaMixin, DeleteView): |
|
1595 |
template_name = 'chrono/manager_confirm_delete.html' |
|
1596 |
model = UnavailabilityCalendar |
|
1597 |
pk_url_kwarg = 'unavailability_calendar_pk' |
|
1598 | ||
1599 |
def set_agenda(self, **kwargs): |
|
1600 |
self.agenda = get_object_or_404(Agenda, id=kwargs.get('pk'), kind='meetings') |
|
1601 | ||
1602 |
def delete(self, request, *args, **kwargs): |
|
1603 |
self.object = self.get_object() |
|
1604 |
self.agenda.unavailability_calendars.remove(self.object) |
|
1605 |
return HttpResponseRedirect(self.get_success_url()) |
|
1606 | ||
1607 | ||
1608 |
agenda_delete_unavailability_calendar = AgendaUnavailabilityCalendarDeleteView.as_view() |
|
1609 | ||
1610 | ||
1533 | 1611 |
class AgendaAddMeetingTypeView(ManagedAgendaMixin, CreateView): |
1534 | 1612 |
template_name = 'chrono/manager_meeting_type_form.html' |
1535 | 1613 |
model = Event |
... | ... | |
1736 | 1814 |
model = TimePeriodException |
1737 | 1815 |
form_class = TimePeriodExceptionForm |
1738 | 1816 | |
1817 |
def get_form_kwargs(self): |
|
1818 |
kwargs = super().get_form_kwargs() |
|
1819 |
kwargs['has_desk'] = True |
|
1820 |
return kwargs |
|
1821 | ||
1739 | 1822 |
def form_valid(self, form): |
1740 | 1823 |
result = super().form_valid(form) |
1741 | 1824 |
exceptions = [self.object] |
... | ... | |
1766 | 1849 |
agenda_add_time_period_exception = AgendaAddTimePeriodExceptionView.as_view() |
1767 | 1850 | |
1768 | 1851 | |
1769 |
class TimePeriodExceptionEditView(ManagedDeskSubobjectMixin, UpdateView):
|
|
1852 |
class TimePeriodExceptionEditView(ManagedTimePeriodExceptionMixin, UpdateView):
|
|
1770 | 1853 |
template_name = 'chrono/manager_time_period_exception_form.html' |
1771 | 1854 |
model = TimePeriodException |
1772 | 1855 |
form_class = TimePeriodExceptionForm |
... | ... | |
1803 | 1886 |
time_period_exception_extract_list = TimePeriodExceptionExtractListView.as_view() |
1804 | 1887 | |
1805 | 1888 | |
1806 |
class TimePeriodExceptionDeleteView(ManagedDeskSubobjectMixin, DeleteView):
|
|
1889 |
class TimePeriodExceptionDeleteView(ManagedTimePeriodExceptionMixin, DeleteView):
|
|
1807 | 1890 |
template_name = 'chrono/manager_confirm_exception_delete.html' |
1808 | 1891 |
model = TimePeriodException |
1809 | 1892 | |
1810 | 1893 |
def get_success_url(self): |
1811 |
referer = self.request.META.get('HTTP_REFERER') |
|
1812 |
success_url = reverse('chrono-manager-time-period-exception-list', kwargs={'pk': self.desk.pk}) |
|
1813 |
if success_url in referer: |
|
1814 |
return success_url |
|
1894 |
if self.desk: |
|
1895 |
referer = self.request.META.get('HTTP_REFERER') |
|
1896 |
success_url = reverse('chrono-manager-time-period-exception-list', kwargs={'pk': self.desk.pk}) |
|
1897 |
if success_url in referer: |
|
1898 |
return success_url |
|
1815 | 1899 | |
1816 | 1900 |
success_url = super(TimePeriodExceptionDeleteView, self).get_success_url() |
1817 |
if 'from_popup' in self.request.GET: |
|
1901 |
if self.desk and 'from_popup' in self.request.GET:
|
|
1818 | 1902 |
success_url = '{}?display_exceptions={}'.format(success_url, self.desk.pk) |
1819 | 1903 |
return success_url |
1820 | 1904 | |
... | ... | |
2073 | 2157 |
time_period_exception_source_toggle = TimePeriodExceptionSourceToggleView.as_view() |
2074 | 2158 | |
2075 | 2159 | |
2160 |
class ViewableUnavailabilityCalendarMixin(object): |
|
2161 |
unavailability_calendar = None |
|
2162 | ||
2163 |
def set_unavailability_calendar(self, **kwargs): |
|
2164 |
self.unavailability_calendar = get_object_or_404(UnavailabilityCalendar, id=kwargs.get('pk')) |
|
2165 | ||
2166 |
def dispatch(self, request, *args, **kwargs): |
|
2167 |
self.set_unavailability_calendar(**kwargs) |
|
2168 |
if not self.check_permissions(request.user): |
|
2169 |
raise PermissionDenied() |
|
2170 |
return super().dispatch(request, *args, **kwargs) |
|
2171 | ||
2172 |
def check_permissions(self, user): |
|
2173 |
return self.unavailability_calendar.can_be_viewed(user) |
|
2174 | ||
2175 |
def get_context_data(self, **kwargs): |
|
2176 |
context = super().get_context_data(**kwargs) |
|
2177 |
context['unavailability_calendar'] = self.unavailability_calendar |
|
2178 |
context['user_can_manage'] = self.unavailability_calendar.can_be_managed(self.request.user) |
|
2179 |
return context |
|
2180 | ||
2181 | ||
2182 |
class ManagedUnavailabilityCalendarMixin(ViewableUnavailabilityCalendarMixin): |
|
2183 |
def check_permissions(self, user): |
|
2184 |
return self.unavailability_calendar.can_be_managed(user) |
|
2185 | ||
2186 |
def get_initial(self): |
|
2187 |
initial = super().get_initial() |
|
2188 |
initial['unavailability_calendar'] = self.unavailability_calendar |
|
2189 |
return initial |
|
2190 | ||
2191 |
def get_success_url(self): |
|
2192 |
return reverse( |
|
2193 |
'chrono-manager-unavailability-calendar-settings', kwargs={'pk': self.unavailability_calendar.id} |
|
2194 |
) |
|
2195 | ||
2196 | ||
2197 |
class UnavailabilityCalendarListView(ListView): |
|
2198 |
template_name = 'chrono/manager_unavailability_calendar_list.html' |
|
2199 |
model = UnavailabilityCalendar |
|
2200 | ||
2201 |
def get_queryset(self): |
|
2202 |
queryset = super().get_queryset() |
|
2203 |
if not self.request.user.is_staff: |
|
2204 |
group_ids = [x.id for x in self.request.user.groups.all()] |
|
2205 |
queryset = queryset.filter(Q(view_role_id__in=group_ids) | Q(edit_role_id__in=group_ids)) |
|
2206 |
return queryset.order_by('label') |
|
2207 | ||
2208 | ||
2209 |
unavailability_calendar_list = UnavailabilityCalendarListView.as_view() |
|
2210 | ||
2211 | ||
2212 |
class UnavailabilityCalendarAddView(CreateView): |
|
2213 |
template_name = 'chrono/manager_unavailability_calendar_form.html' |
|
2214 |
model = UnavailabilityCalendar |
|
2215 |
form_class = UnavailabilityCalendarAddForm |
|
2216 | ||
2217 |
def dispatch(self, request, *args, **kwargs): |
|
2218 |
if not request.user.is_staff: |
|
2219 |
raise PermissionDenied() |
|
2220 |
return super().dispatch(request, *args, **kwargs) |
|
2221 | ||
2222 |
def get_success_url(self): |
|
2223 |
return reverse('chrono-manager-unavailability-calendar-view', kwargs={'pk': self.object.id}) |
|
2224 | ||
2225 | ||
2226 |
unavailability_calendar_add = UnavailabilityCalendarAddView.as_view() |
|
2227 | ||
2228 | ||
2229 |
class UnavailabilityCalendarDetailView(ViewableUnavailabilityCalendarMixin, DetailView): |
|
2230 |
template_name = 'chrono/manager_unavailability_calendar_detail.html' |
|
2231 |
model = UnavailabilityCalendar |
|
2232 | ||
2233 | ||
2234 |
unavailability_calendar_view = UnavailabilityCalendarDetailView.as_view() |
|
2235 | ||
2236 | ||
2237 |
class UnavailabilityCalendarEditView(ManagedUnavailabilityCalendarMixin, UpdateView): |
|
2238 |
template_name = 'chrono/manager_unavailability_calendar_form.html' |
|
2239 |
model = UnavailabilityCalendar |
|
2240 |
form_class = UnavailabilityCalendarEditForm |
|
2241 | ||
2242 | ||
2243 |
unavailability_calendar_edit = UnavailabilityCalendarEditView.as_view() |
|
2244 | ||
2245 | ||
2246 |
class UnavailabilityCalendarDeleteView(DeleteView): |
|
2247 |
template_name = 'chrono/manager_confirm_delete.html' |
|
2248 |
model = UnavailabilityCalendar |
|
2249 | ||
2250 |
def dispatch(self, request, *args, **kwargs): |
|
2251 |
if not request.user.is_staff: |
|
2252 |
raise PermissionDenied() |
|
2253 |
return super().dispatch(request, *args, **kwargs) |
|
2254 | ||
2255 |
def get_success_url(self): |
|
2256 |
return reverse('chrono-manager-unavailability-calendar-list') |
|
2257 | ||
2258 | ||
2259 |
unavailability_calendar_delete = UnavailabilityCalendarDeleteView.as_view() |
|
2260 | ||
2261 | ||
2262 |
class UnavailabilityCalendarSettings(ManagedUnavailabilityCalendarMixin, DetailView): |
|
2263 |
template_name = 'chrono/manager_unavailability_calendar_settings.html' |
|
2264 |
model = UnavailabilityCalendar |
|
2265 | ||
2266 |
def get_context_data(self, **kwargs): |
|
2267 |
context = super(UnavailabilityCalendarSettings, self).get_context_data(**kwargs) |
|
2268 |
context['unavailability_calendar'] = self.object |
|
2269 |
return context |
|
2270 | ||
2271 | ||
2272 |
unavailability_calendar_settings = UnavailabilityCalendarSettings.as_view() |
|
2273 | ||
2274 | ||
2275 |
class UnavailabilityCalendarAddUnavailabilityView(ManagedUnavailabilityCalendarMixin, CreateView): |
|
2276 |
template_name = 'chrono/manager_time_period_exception_form.html' |
|
2277 |
form_class = TimePeriodExceptionForm |
|
2278 | ||
2279 |
def get_form_kwargs(self): |
|
2280 |
kwargs = super().get_form_kwargs() |
|
2281 |
kwargs['has_desk'] = False |
|
2282 |
return kwargs |
|
2283 | ||
2284 |
def get_context_data(self, **kwargs): |
|
2285 |
context = super(UnavailabilityCalendarAddUnavailabilityView, self).get_context_data(**kwargs) |
|
2286 |
context['cancel_url'] = reverse( |
|
2287 |
'chrono-manager-unavailability-calendar-settings', kwargs={'pk': self.unavailability_calendar.pk,} |
|
2288 |
) |
|
2289 |
return context |
|
2290 | ||
2291 | ||
2292 |
unavailability_calendar_add_unavailability = UnavailabilityCalendarAddUnavailabilityView.as_view() |
|
2293 | ||
2294 | ||
2076 | 2295 |
def menu_json(request): |
2077 | 2296 |
label = _('Agendas') |
2078 | 2297 |
json_str = json.dumps( |
chrono/urls_utils.py | ||
---|---|---|
22 | 22 |
from django.core.exceptions import PermissionDenied |
23 | 23 |
from django.db.models import Q |
24 | 24 | |
25 |
from .agendas.models import Agenda |
|
25 |
from .agendas.models import Agenda, UnavailabilityCalendar
|
|
26 | 26 | |
27 | 27 |
if django.VERSION < (2, 0, 0): |
28 | 28 |
from django.urls.resolvers import RegexURLPattern as URLPattern |
... | ... | |
55 | 55 |
if user and not user.is_anonymous: |
56 | 56 |
# /manage/ is open to anyone authorized to view or edit an agenda. |
57 | 57 |
group_ids = [x.id for x in user.groups.all()] |
58 |
if Agenda.objects.filter(Q(view_role_id__in=group_ids) | Q(edit_role_id__in=group_ids)).exists(): |
|
58 |
if ( |
|
59 |
Agenda.objects.filter(Q(view_role_id__in=group_ids) | Q(edit_role_id__in=group_ids)).exists() |
|
60 |
or UnavailabilityCalendar.objects.filter( |
|
61 |
Q(view_role_id__in=group_ids) | Q(edit_role_id__in=group_ids) |
|
62 |
).exists() |
|
63 |
): |
|
59 | 64 |
return True |
60 | 65 |
raise PermissionDenied() |
61 | 66 |
# As the last resort, show the login form |
tests/test_api.py | ||
---|---|---|
21 | 21 |
Resource, |
22 | 22 |
TimePeriod, |
23 | 23 |
TimePeriodException, |
24 |
UnavailabilityCalendar, |
|
24 | 25 |
VirtualMember, |
25 | 26 |
) |
26 | 27 |
import chrono.api.views |
... | ... | |
643 | 644 |
) |
644 | 645 |
with CaptureQueriesContext(connection) as ctx: |
645 | 646 |
resp = app.get(api_url) |
646 |
assert len(ctx.captured_queries) == 9
|
|
647 |
assert len(ctx.captured_queries) == 10
|
|
647 | 648 |
assert len(resp.json['data']) == 32 |
648 | 649 |
assert [s['datetime'] for s in resp.json['data'] if s['disabled'] is True] == [ |
649 | 650 |
'%s 09:00:00' % tomorrow_str, |
... | ... | |
3802 | 3803 |
with CaptureQueriesContext(connection) as ctx: |
3803 | 3804 |
resp = app.get(api_url) |
3804 | 3805 |
assert len(resp.json['data']) == 12 |
3805 |
assert len(ctx.captured_queries) == 11
|
|
3806 |
assert len(ctx.captured_queries) == 12
|
|
3806 | 3807 | |
3807 | 3808 |
# simulate booking |
3808 | 3809 |
dt = datetime.datetime.strptime(resp.json['data'][2]['id'].split(':')[1], '%Y-%m-%d-%H%M') |
... | ... | |
4126 | 4127 |
ics = app.get(resp.json['api']['ics_url']).text |
4127 | 4128 |
assert 'DTSTART:20170519T231200Z' in ics |
4128 | 4129 |
assert 'DTEND:20170520T004200Z' in ics |
4130 | ||
4131 | ||
4132 |
def test_unavailabilitycalendar_meetings_datetimes(app, user, meetings_agenda): |
|
4133 |
app.authorization = ('Basic', ('john.doe', 'password')) |
|
4134 |
meeting_type = meetings_agenda.meetingtype_set.first() |
|
4135 |
datetimes_url = '/api/agenda/%s/meetings/%s/datetimes/' % (meetings_agenda.slug, meeting_type.slug) |
|
4136 |
resp = app.get(datetimes_url) |
|
4137 |
assert len(resp.json['data']) == 144 |
|
4138 | ||
4139 |
# create an unvalailability calendar |
|
4140 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='foo holydays') |
|
4141 |
TimePeriodException.objects.create( |
|
4142 |
unavailability_calendar=unavailability_calendar, |
|
4143 |
start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 0)), |
|
4144 |
end_datetime=make_aware(datetime.datetime(2017, 5, 22, 11, 0)), |
|
4145 |
) |
|
4146 |
unavailability_calendar.agendas.add(meetings_agenda) |
|
4147 | ||
4148 |
# 2 slots are gone |
|
4149 |
resp2 = app.get(datetimes_url) |
|
4150 |
assert len(resp.json['data']) == len(resp2.json['data']) + 2 |
|
4151 | ||
4152 |
# add a standard desk exception |
|
4153 |
desk = meetings_agenda.desk_set.first() |
|
4154 |
TimePeriodException.objects.create( |
|
4155 |
desk=desk, |
|
4156 |
start_datetime=make_aware(datetime.datetime(2017, 5, 22, 11, 0)), |
|
4157 |
end_datetime=make_aware(datetime.datetime(2017, 5, 22, 12, 0)), |
|
4158 |
) |
|
4159 |
# 4 slots are gone |
|
4160 |
resp3 = app.get(datetimes_url) |
|
4161 |
assert len(resp.json['data']) == len(resp3.json['data']) + 4 |
|
4162 | ||
4163 | ||
4164 |
def test_unavailabilitycalendar_on_virtual_datetimes(app, user, mock_now): |
|
4165 |
foo_agenda = Agenda.objects.create(label='Foo Meeting', kind='meetings', maximal_booking_delay=7) |
|
4166 |
MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30) |
|
4167 |
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1') |
|
4168 |
TimePeriod.objects.create( |
|
4169 |
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1, |
|
4170 |
) |
|
4171 |
TimePeriod.objects.create( |
|
4172 |
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1, |
|
4173 |
) |
|
4174 |
virt_agenda = Agenda.objects.create(label='Virtual Agenda', kind='virtual') |
|
4175 |
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda) |
|
4176 | ||
4177 |
api_url = '/api/agenda/%s/meetings/meeting-type/datetimes/' % (virt_agenda.slug) |
|
4178 |
resp = app.get(api_url) |
|
4179 |
# 8 slots |
|
4180 |
data = resp.json['data'] |
|
4181 |
assert len(data) == 8 |
|
4182 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
4183 |
assert data[1]['datetime'] == '2017-05-22 10:30:00' |
|
4184 |
assert data[2]['datetime'] == '2017-05-22 11:00:00' |
|
4185 | ||
4186 |
# exclude one hour the first day through an unvalailability calendar on the foo agenda |
|
4187 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='foo holydays') |
|
4188 |
TimePeriodException.objects.create( |
|
4189 |
unavailability_calendar=unavailability_calendar, |
|
4190 |
start_datetime=make_aware(datetime.datetime(2017, 5, 22, 11, 0)), |
|
4191 |
end_datetime=make_aware(datetime.datetime(2017, 5, 22, 12, 0)), |
|
4192 |
) |
|
4193 |
unavailability_calendar.agendas.add(foo_agenda) |
|
4194 | ||
4195 |
resp = app.get(api_url) |
|
4196 |
data = resp.json['data'] |
|
4197 |
assert len(data) == 6 |
|
4198 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
4199 |
assert data[1]['datetime'] == '2017-05-22 10:30:00' |
|
4200 |
# no more slots the 22 thanks to the unavailability calendar |
|
4201 |
assert data[2]['datetime'] == '2017-05-23 10:00:00' |
|
4202 | ||
4203 |
# exclude the second day |
|
4204 |
TimePeriodException.objects.create( |
|
4205 |
unavailability_calendar=unavailability_calendar, |
|
4206 |
start_datetime=make_aware(datetime.datetime(2017, 5, 23, 9, 0)), |
|
4207 |
end_datetime=make_aware(datetime.datetime(2017, 5, 23, 18, 0)), |
|
4208 |
) |
|
4209 |
resp = app.get(api_url) |
|
4210 |
data = resp.json['data'] |
|
4211 |
assert len(data) == 2 |
|
4212 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
4213 |
assert data[1]['datetime'] == '2017-05-22 10:30:00' |
|
4214 | ||
4215 |
# add a second real agenda |
|
4216 |
bar_agenda = Agenda.objects.create(label='Bar Meeting', kind='meetings', maximal_booking_delay=7) |
|
4217 |
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=bar_agenda) |
|
4218 |
MeetingType.objects.create(agenda=bar_agenda, label='Meeting Type', duration=30) |
|
4219 |
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1') |
|
4220 |
bar_desk_2 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 2') |
|
4221 |
TimePeriod.objects.create( |
|
4222 |
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1, |
|
4223 |
) |
|
4224 |
TimePeriod.objects.create( |
|
4225 |
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1, |
|
4226 |
) |
|
4227 |
TimePeriod.objects.create( |
|
4228 |
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_2, |
|
4229 |
) |
|
4230 |
TimePeriod.objects.create( |
|
4231 |
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_2, |
|
4232 |
) |
|
4233 | ||
4234 |
# bar_agenda has the same time periods than foo_agenda, but no unavailability calendar |
|
4235 |
# so we are back at the start : 8 slots |
|
4236 |
resp = app.get(api_url) |
|
4237 |
data = resp.json['data'] |
|
4238 |
assert len(data) == 8 |
|
4239 | ||
4240 |
# exclude one hour the second day through another unvalailability calendar on the bar agenda |
|
4241 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='bar holydays') |
|
4242 |
TimePeriodException.objects.create( |
|
4243 |
unavailability_calendar=unavailability_calendar, |
|
4244 |
start_datetime=make_aware(datetime.datetime(2017, 5, 23, 11, 0)), |
|
4245 |
end_datetime=make_aware(datetime.datetime(2017, 5, 23, 12, 0)), |
|
4246 |
) |
|
4247 |
unavailability_calendar.agendas.add(bar_agenda) |
|
4248 | ||
4249 |
# 2 slots are gone |
|
4250 |
resp = app.get(api_url) |
|
4251 |
data = resp.json['data'] |
|
4252 |
assert len(data) == 6 |
|
4253 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
4254 |
assert data[-1]['datetime'] == '2017-05-23 10:30:00' |
tests/test_manager.py | ||
---|---|---|
35 | 35 |
TimePeriodExceptionSource, |
36 | 36 |
VirtualMember, |
37 | 37 |
AgendaReminderSettings, |
38 |
UnavailabilityCalendar, |
|
38 | 39 |
) |
39 | 40 |
from chrono.manager.forms import TimePeriodExceptionForm |
40 | 41 |
from chrono.utils.signature import check_query |
... | ... | |
1000 | 1001 |
app = login(app) |
1001 | 1002 |
with CaptureQueriesContext(connection) as ctx: |
1002 | 1003 |
app.get('/manage/agendas/%s/settings' % agenda.pk) |
1003 |
assert len(ctx.captured_queries) == 9
|
|
1004 |
assert len(ctx.captured_queries) == 10
|
|
1004 | 1005 | |
1005 | 1006 | |
1006 | 1007 |
def test_agenda_resources(app, admin_user): |
... | ... | |
4366 | 4367 |
'Reminder: you have a booking for event "{{ event_label }}", on 02/06 at 2:30 p.m.. Take ID card.' |
4367 | 4368 |
in resp.text |
4368 | 4369 |
) |
4370 | ||
4371 | ||
4372 |
def test_no_unavailability_calendar(app, admin_user): |
|
4373 |
app = login(app) |
|
4374 | ||
4375 |
# empty unavailability calendars list |
|
4376 |
resp = app.get('/manage/') |
|
4377 |
resp = resp.click('Unavailability calendars') |
|
4378 |
assert "This site doesn't have any unavailability calendar yet" in resp.text |
|
4379 | ||
4380 |
# on the agenda settings page, no unavailability calendar reference |
|
4381 |
agenda = Agenda.objects.create(label='Agenda', kind='meetings') |
|
4382 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
4383 |
assert 'unavailability calendar' not in resp.text |
|
4384 | ||
4385 | ||
4386 |
def test_add_unavailability_calendar(app, admin_user): |
|
4387 |
app = login(app) |
|
4388 |
resp = app.get('/manage/') |
|
4389 |
resp = resp.click('Unavailability calendars') |
|
4390 |
resp = resp.click('New') |
|
4391 |
resp.form['label'] = 'Foo bar' |
|
4392 |
resp = resp.form.submit() |
|
4393 |
unavailability_calendar = UnavailabilityCalendar.objects.latest('pk') |
|
4394 |
assert resp.location.endswith('/manage/unavailability-calendar/%s/' % unavailability_calendar.pk) |
|
4395 |
assert unavailability_calendar.label == 'Foo bar' |
|
4396 |
assert unavailability_calendar.slug == 'foo-bar' |
|
4397 |
resp = resp.follow() |
|
4398 |
assert 'This unavailability calendar is not used yet.' in resp.text |
|
4399 |
resp = app.get('/manage/unavailability-calendars/') |
|
4400 |
assert 'Foo bar' in resp.text |
|
4401 |
assert 'foo-bar' in resp.text |
|
4402 | ||
4403 | ||
4404 |
def test_edit_unavailability_calendar(app, admin_user): |
|
4405 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4406 |
app = login(app) |
|
4407 |
settings_url = '/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk |
|
4408 |
resp = app.get(settings_url) |
|
4409 |
resp = resp.click('Options') |
|
4410 |
resp.form['label'] = 'Bar' |
|
4411 |
resp = resp.form.submit() |
|
4412 |
assert resp.location.endswith(settings_url) |
|
4413 |
resp = resp.follow() |
|
4414 |
assert 'Bar' in resp.text |
|
4415 |
unavailability_calendar.refresh_from_db() |
|
4416 |
assert unavailability_calendar.label == 'Bar' |
|
4417 | ||
4418 | ||
4419 |
def test_delete_unavailability_calendar(app, admin_user): |
|
4420 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4421 |
app = login(app) |
|
4422 |
resp = app.get('/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk) |
|
4423 |
resp = resp.click('Delete') |
|
4424 |
resp = resp.form.submit() |
|
4425 |
assert resp.location.endswith('/manage/unavailability-calendars/') |
|
4426 |
resp = resp.follow() |
|
4427 |
assert 'Foo' not in resp.text |
|
4428 |
assert UnavailabilityCalendar.objects.count() == 0 |
|
4429 | ||
4430 | ||
4431 |
def test_unavailability_calendar_add_time_period_exeptions(app, admin_user): |
|
4432 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4433 |
app = login(app) |
|
4434 |
resp = app.get('/manage/unavailability-calendar/%s/' % unavailability_calendar.pk) |
|
4435 |
resp = resp.click('Settings') |
|
4436 |
assert 'There is no unavailabilities yet' in resp.text |
|
4437 |
resp = resp.click('Add Unavailability') |
|
4438 |
today = datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0) |
|
4439 |
tomorrow = make_aware(today + datetime.timedelta(days=1)) |
|
4440 |
resp.form['label'] = 'Exception 1' |
|
4441 |
resp.form['start_datetime_0'] = tomorrow.strftime('%Y-%m-%d') |
|
4442 |
resp.form['start_datetime_1'] = '08:00' |
|
4443 |
resp.form['end_datetime_0'] = tomorrow.strftime('%Y-%m-%d') |
|
4444 |
resp.form['end_datetime_1'] = '16:00' |
|
4445 |
resp = resp.form.submit().follow() |
|
4446 |
assert 'Exception 1' in resp.text |
|
4447 | ||
4448 |
time_period_exception = TimePeriodException.objects.first() |
|
4449 |
assert time_period_exception.unavailability_calendar == unavailability_calendar |
|
4450 |
assert time_period_exception.desk is None |
|
4451 |
assert time_period_exception.label == 'Exception 1' |
|
4452 | ||
4453 | ||
4454 |
def test_unavailability_calendar_edit_time_period_exeptions(app, admin_user): |
|
4455 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4456 |
time_period_exception = TimePeriodException.objects.create( |
|
4457 |
unavailability_calendar=unavailability_calendar, |
|
4458 |
label='Exception 1', |
|
4459 |
start_datetime=now() - datetime.timedelta(days=2), |
|
4460 |
end_datetime=now() - datetime.timedelta(days=1), |
|
4461 |
) |
|
4462 | ||
4463 |
app = login(app) |
|
4464 |
resp = app.get('/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk) |
|
4465 |
assert 'Exception 1' in resp.text |
|
4466 |
resp = resp.click(href='/manage/time-period-exceptions/%s/edit' % time_period_exception.pk) |
|
4467 |
resp.form['label'] = 'Exception foo' |
|
4468 |
resp = resp.form.submit().follow() |
|
4469 |
assert 'Exception foo' in resp.text |
|
4470 |
time_period_exception.refresh_from_db() |
|
4471 |
assert 'Exception foo' == time_period_exception.label |
|
4472 | ||
4473 | ||
4474 |
def test_unavailability_calendar_delete_time_period_exeptions(app, admin_user): |
|
4475 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4476 |
time_period_exception = TimePeriodException.objects.create( |
|
4477 |
unavailability_calendar=unavailability_calendar, |
|
4478 |
label='Exception 1', |
|
4479 |
start_datetime=now() - datetime.timedelta(days=2), |
|
4480 |
end_datetime=now() - datetime.timedelta(days=1), |
|
4481 |
) |
|
4482 | ||
4483 |
app = login(app) |
|
4484 |
resp = app.get('/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk) |
|
4485 |
assert 'Exception 1' in resp.text |
|
4486 |
resp = resp.click(href='/manage/time-period-exceptions/%s/delete' % time_period_exception.pk) |
|
4487 |
resp = resp.form.submit().follow() |
|
4488 |
assert 'Exception foo' not in resp.text |
|
4489 |
assert unavailability_calendar.timeperiodexception_set.count() == 0 |
|
4490 | ||
4491 | ||
4492 |
def test_add_unavailability_calendar_in_agenda(app, admin_user): |
|
4493 |
app = login(app) |
|
4494 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4495 |
agenda = Agenda.objects.create(label='Agenda', kind='meetings') |
|
4496 |
settings_url = '/manage/agendas/%s/settings' % agenda.pk |
|
4497 |
resp = app.get(settings_url) |
|
4498 |
assert "This agenda doesn't have any unavailability calendar yet" in resp.text |
|
4499 |
resp = resp.click('Add unavailability calendar') |
|
4500 |
resp.form['unavailability_calendar'].select(unavailability_calendar.pk) |
|
4501 |
resp = resp.form.submit() |
|
4502 |
assert resp.location == settings_url |
|
4503 |
resp = resp.follow() |
|
4504 |
assert 'Foo' in resp.text |
|
4505 |
assert agenda.unavailability_calendars.first() == unavailability_calendar |
|
4506 |
resp = resp.click(href='/manage/unavailability-calendar/%s/' % unavailability_calendar.pk) |
|
4507 |
assert 'Agenda' in resp.text |
|
4508 |
resp = resp.click(href='/manage/agendas/%s/' % agenda.pk) |
|
4509 | ||
4510 | ||
4511 |
def test_remove_unavailability_calendar_in_agenda(app, admin_user): |
|
4512 |
app = login(app) |
|
4513 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4514 |
agenda = Agenda.objects.create(label='Agenda', kind='meetings') |
|
4515 |
unavailability_calendar.agendas.add(agenda) |
|
4516 |
settings_url = '/manage/agendas/%s/settings' % agenda.pk |
|
4517 |
resp = app.get(settings_url) |
|
4518 |
assert "Foo" in resp.text |
|
4519 |
resp = resp.click( |
|
4520 |
href='/manage/agendas/%s/unavailability-calendar/%s/delete' % (agenda.pk, unavailability_calendar.pk) |
|
4521 |
) |
|
4522 |
resp = resp.form.submit() |
|
4523 |
assert resp.location.endswith(settings_url) |
|
4524 |
resp = resp.follow() |
|
4525 |
assert "Foo" not in resp.text |
|
4526 |
assert agenda.unavailability_calendars.count() == 0 |
|
4527 | ||
4528 | ||
4529 |
def test_unavailability_calendar_homepage_permission(app, manager_user): |
|
4530 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4531 |
app = login(app, username='manager', password='manager') |
|
4532 |
resp = app.get('/manage/', status=403) |
|
4533 |
group = manager_user.groups.all()[0] |
|
4534 |
unavailability_calendar.view_role = group |
|
4535 |
unavailability_calendar.edit_role = None |
|
4536 |
unavailability_calendar.save() |
|
4537 |
resp = app.get('/manage/') |
|
4538 |
resp = resp.click('Unavailability calendars') |
|
4539 |
unavailability_calendar.view_role = None |
|
4540 |
unavailability_calendar.edit_role = group |
|
4541 |
unavailability_calendar.save() |
|
4542 |
resp = app.get('/manage/') |
|
4543 |
resp = resp.click('Unavailability calendars') |
|
4544 | ||
4545 | ||
4546 |
def test_unavailability_calendar_list_permissions(app, manager_user): |
|
4547 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4548 |
app = login(app, username='manager', password='manager') |
|
4549 |
app.get('/manage/unavailability-calendars/', status=403) |
|
4550 |
group = manager_user.groups.all()[0] |
|
4551 |
unavailability_calendar.view_role = group |
|
4552 |
unavailability_calendar.edit_role = None |
|
4553 |
unavailability_calendar.save() |
|
4554 |
resp = app.get('/manage/unavailability-calendars/') |
|
4555 |
assert 'Calendar 1' in resp.text |
|
4556 |
assert 'New' not in resp.text |
|
4557 |
unavailability_calendar.view_role = None |
|
4558 |
unavailability_calendar.edit_role = group |
|
4559 |
unavailability_calendar.save() |
|
4560 |
assert 'Calendar 1' in resp.text |
|
4561 |
assert 'New' not in resp.text |
|
4562 | ||
4563 | ||
4564 |
def test_unavailability_calendar_add_permissions(app, manager_user): |
|
4565 |
app = login(app, username='manager', password='manager') |
|
4566 |
url = '/manage/unavailability-calendar/add/' |
|
4567 |
app.get(url, status=403) |
|
4568 | ||
4569 | ||
4570 |
def test_unavailability_calendar_detail_permissions(app, manager_user): |
|
4571 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4572 |
app = login(app, username='manager', password='manager') |
|
4573 |
detail_url = '/manage/unavailability-calendar/%s/' % unavailability_calendar.pk |
|
4574 |
resp = app.get(detail_url, status=403) |
|
4575 |
group = manager_user.groups.all()[0] |
|
4576 |
unavailability_calendar.view_role = group |
|
4577 |
unavailability_calendar.edit_role = None |
|
4578 |
unavailability_calendar.save() |
|
4579 |
resp = app.get(detail_url) |
|
4580 |
assert 'Settings' not in resp.text |
|
4581 |
unavailability_calendar.view_role = None |
|
4582 |
unavailability_calendar.edit_role = group |
|
4583 |
unavailability_calendar.save() |
|
4584 |
resp = app.get(detail_url) |
|
4585 |
assert 'Settings' in resp.text |
|
4586 | ||
4587 | ||
4588 |
def test_unavailability_calendar_edit_permissions(app, manager_user): |
|
4589 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4590 |
app = login(app, username='manager', password='manager') |
|
4591 |
url = '/manage/unavailability-calendar/%s/edit/' % unavailability_calendar.pk |
|
4592 |
app.get(url, status=403) |
|
4593 |
group = manager_user.groups.all()[0] |
|
4594 |
unavailability_calendar.view_role = group |
|
4595 |
unavailability_calendar.edit_role = None |
|
4596 |
unavailability_calendar.save() |
|
4597 |
app.get(url, status=403) |
|
4598 |
unavailability_calendar.view_role = None |
|
4599 |
unavailability_calendar.edit_role = group |
|
4600 |
unavailability_calendar.save() |
|
4601 |
app.get(url) |
|
4602 | ||
4603 | ||
4604 |
def test_unavailability_calendar_delete_permissions(app, manager_user): |
|
4605 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4606 |
app = login(app, username='manager', password='manager') |
|
4607 |
url = '/manage/unavailability-calendar/%s/delete/' % unavailability_calendar.pk |
|
4608 |
app.get(url, status=403) |
|
4609 |
group = manager_user.groups.all()[0] |
|
4610 |
unavailability_calendar.view_role = group |
|
4611 |
unavailability_calendar.edit_role = None |
|
4612 |
unavailability_calendar.save() |
|
4613 |
app.get(url, status=403) |
|
4614 |
unavailability_calendar.view_role = None |
|
4615 |
unavailability_calendar.edit_role = group |
|
4616 |
unavailability_calendar.save() |
|
4617 |
app.get(url, status=403) |
|
4618 | ||
4619 | ||
4620 |
def test_unavailability_calendar_settings_permissions(app, manager_user): |
|
4621 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4622 |
app = login(app, username='manager', password='manager') |
|
4623 |
settings_url = '/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk |
|
4624 |
app.get(settings_url, status=403) |
|
4625 |
group = manager_user.groups.all()[0] |
|
4626 |
unavailability_calendar.view_role = group |
|
4627 |
unavailability_calendar.edit_role = None |
|
4628 |
unavailability_calendar.save() |
|
4629 |
app.get(settings_url, status=403) |
|
4630 |
unavailability_calendar.view_role = None |
|
4631 |
unavailability_calendar.edit_role = group |
|
4632 |
unavailability_calendar.save() |
|
4633 |
app.get(settings_url) |
|
4634 | ||
4635 | ||
4636 |
def test_unavailability_calendar_add_unavailability_permissions(app, manager_user): |
|
4637 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4638 |
app = login(app, username='manager', password='manager') |
|
4639 |
url = '/manage/unavailability-calendar/%s/add-unavailability' % unavailability_calendar.pk |
|
4640 |
app.get(url, status=403) |
|
4641 |
group = manager_user.groups.all()[0] |
|
4642 |
unavailability_calendar.view_role = group |
|
4643 |
unavailability_calendar.edit_role = None |
|
4644 |
unavailability_calendar.save() |
|
4645 |
app.get(url, status=403) |
|
4646 |
unavailability_calendar.view_role = None |
|
4647 |
unavailability_calendar.edit_role = group |
|
4648 |
unavailability_calendar.save() |
|
4649 |
app.get(url) |
|
4650 | ||
4651 | ||
4652 |
def test_unavailability_calendar_edit_unavailability_permissions(app, manager_user): |
|
4653 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4654 |
time_period_exception = TimePeriodException.objects.create( |
|
4655 |
unavailability_calendar=unavailability_calendar, |
|
4656 |
start_datetime=now() - datetime.timedelta(days=2), |
|
4657 |
end_datetime=now() - datetime.timedelta(days=1), |
|
4658 |
) |
|
4659 |
app = login(app, username='manager', password='manager') |
|
4660 |
url = '/manage/time-period-exceptions/%s/edit' % time_period_exception.pk |
|
4661 |
app.get(url, status=403) |
|
4662 |
group = manager_user.groups.all()[0] |
|
4663 |
unavailability_calendar.view_role = group |
|
4664 |
unavailability_calendar.edit_role = None |
|
4665 |
unavailability_calendar.save() |
|
4666 |
app.get(url, status=403) |
|
4667 |
unavailability_calendar.view_role = None |
|
4668 |
unavailability_calendar.edit_role = group |
|
4669 |
unavailability_calendar.save() |
|
4670 |
app.get(url) |
|
4671 | ||
4672 | ||
4673 |
def test_unavailability_calendar_delete_unavailability_permissions(app, manager_user): |
|
4674 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4675 |
time_period_exception = TimePeriodException.objects.create( |
|
4676 |
unavailability_calendar=unavailability_calendar, |
|
4677 |
start_datetime=now() - datetime.timedelta(days=2), |
|
4678 |
end_datetime=now() - datetime.timedelta(days=1), |
|
4679 |
) |
|
4680 |
app = login(app, username='manager', password='manager') |
|
4681 |
url = '/manage/time-period-exceptions/%s/delete' % time_period_exception.pk |
|
4682 |
app.get(url, status=403) |
|
4683 |
group = manager_user.groups.all()[0] |
|
4684 |
unavailability_calendar.view_role = group |
|
4685 |
unavailability_calendar.edit_role = None |
|
4686 |
unavailability_calendar.save() |
|
4687 |
app.get(url, status=403) |
|
4688 |
unavailability_calendar.view_role = None |
|
4689 |
unavailability_calendar.edit_role = group |
|
4690 |
unavailability_calendar.save() |
|
4691 |
app.get(url) |
|
4369 |
- |