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-10-05 12:37 |
|
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 |
('desks', models.ManyToManyField(related_name='unavailability_calendars', to='agendas.Desk')), |
|
27 |
( |
|
28 |
'edit_role', |
|
29 |
models.ForeignKey( |
|
30 |
blank=True, |
|
31 |
default=None, |
|
32 |
null=True, |
|
33 |
on_delete=django.db.models.deletion.SET_NULL, |
|
34 |
related_name='+', |
|
35 |
to='auth.Group', |
|
36 |
verbose_name='Edit Role', |
|
37 |
), |
|
38 |
), |
|
39 |
( |
|
40 |
'view_role', |
|
41 |
models.ForeignKey( |
|
42 |
blank=True, |
|
43 |
default=None, |
|
44 |
null=True, |
|
45 |
on_delete=django.db.models.deletion.SET_NULL, |
|
46 |
related_name='+', |
|
47 |
to='auth.Group', |
|
48 |
verbose_name='View Role', |
|
49 |
), |
|
50 |
), |
|
51 |
], |
|
52 |
options={'ordering': ['label'],}, |
|
53 |
), |
|
54 |
migrations.AlterField( |
|
55 |
model_name='timeperiodexception', |
|
56 |
name='desk', |
|
57 |
field=models.ForeignKey( |
|
58 |
null=True, on_delete=django.db.models.deletion.CASCADE, to='agendas.Desk' |
|
59 |
), |
|
60 |
), |
|
61 |
migrations.AddField( |
|
62 |
model_name='timeperiodexception', |
|
63 |
name='unavailability_calendar', |
|
64 |
field=models.ForeignKey( |
|
65 |
null=True, on_delete=django.db.models.deletion.CASCADE, to='agendas.UnavailabilityCalendar' |
|
66 |
), |
|
67 |
), |
|
68 |
] |
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 |
desks = models.ManyToManyField(Desk, 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__desks__agenda__in=agendas |
|
148 |
).order_by('start_datetime', 'end_datetime'): |
|
149 |
for desk in time_period_exception.unavailability_calendar.desks.all(): |
|
150 |
if desk not in desks_exceptions: |
|
151 |
desks_exceptions[desk] = IntervalSet() |
|
152 |
desks_exceptions[desk].add( |
|
153 |
time_period_exception.start_datetime, time_period_exception.end_datetime |
|
154 |
) |
|
155 | ||
144 | 156 |
# compute reduced min/max_datetime windows by desks based on exceptions |
145 | 157 |
desk_min_max_datetime = {} |
146 | 158 |
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 |
... | ... | |
272 | 292 |
super().__init__(*args, **kwargs) |
273 | 293 |
if self.instance.pk is not None: |
274 | 294 |
del self.fields['all_desks'] |
295 |
elif self.instance.unavailability_calendar: |
|
296 |
del self.fields['all_desks'] |
|
275 | 297 |
elif self.instance.desk_id and self.instance.desk.agenda.desk_set.count() == 1: |
276 | 298 |
del self.fields['all_desks'] |
277 | 299 |
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_import_exceptions.html | ||
---|---|---|
13 | 13 |
{% block content %} |
14 | 14 | |
15 | 15 |
<form method="post" enctype="multipart/form-data"> |
16 |
{% if exception_sources %} |
|
16 |
{% if exception_sources or unavailability_calendars %}
|
|
17 | 17 |
<ul class="objects-list single-links"> |
18 | 18 |
{% for object in exception_sources %} |
19 | 19 |
<li> |
... | ... | |
30 | 30 |
{% endif %} |
31 | 31 |
</li> |
32 | 32 |
{% endfor %} |
33 |
{% for unavailability_calendar in unavailability_calendars %} |
|
34 |
<li> |
|
35 |
<a {% if not unavailability_calendar.enabled %}class="disabled"{% endif %} title="{{ unavailability_calendar }}" href="{{ unavailability_calendar.get_absolute_url }}">{{ unavailability_calendar|truncatechars:50 }}</a> |
|
36 |
<a class="link-action-text" href="{% url 'chrono-manager-unavailability-calendar-toggle-view' desk.pk unavailability_calendar.pk %}">({{ unavailability_calendar.enabled|yesno:_("disable,enable") }})</a> |
|
37 |
</li> |
|
38 |
{% endfor %} |
|
33 | 39 |
</ul> |
34 | 40 |
{% endif %} |
35 | 41 |
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 |
- {{ unavailability_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 |
{% if agendas %} |
|
32 |
<ul class="objects-list single-links"> |
|
33 |
{% for agenda in agendas %} |
|
34 |
<li> |
|
35 |
<a href="{% url 'chrono-manager-agenda-view' pk=agenda.pk %}"> |
|
36 |
{{ agenda.label }} |
|
37 |
</a> |
|
38 |
</li> |
|
39 |
{% endfor %} |
|
40 |
</ul> |
|
41 |
{% else %} |
|
42 |
<div class="big-msg-info"> |
|
43 |
{% blocktrans %} |
|
44 |
This unavailability calendar is not used yet. |
|
45 |
{% endblocktrans %} |
|
46 |
</div> |
|
47 |
{% endif %} |
|
48 |
</div> |
|
49 |
</div> |
|
50 | ||
51 |
{% 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 |
</h2> |
|
12 |
<span class="actions"> |
|
13 |
<a class="extra-actions-menu-opener"></a> |
|
14 |
{% block agenda-extra-management-actions %} |
|
15 |
<a rel="popup" href="{% url 'chrono-manager-unavailability-calendar-add-unavailability' pk=unavailability_calendar.id %}">{% trans 'Add Unavailability' %}</a> |
|
16 |
{% endblock %} |
|
17 |
<ul class="extra-actions-menu"> |
|
18 |
<li><a rel="popup" href="{% url 'chrono-manager-unavailability-calendar-edit' pk=unavailability_calendar.id %}">{% trans 'Options' %}</a></li> |
|
19 |
{% if user.is_staff %} |
|
20 |
<li><a rel="popup" href="{% url 'chrono-manager-unavailability-calendar-delete' pk=unavailability_calendar.id %}">{% trans 'Delete' %}</a></li> |
|
21 |
{% endif %} |
|
22 |
</ul> |
|
23 |
</span> |
|
24 |
{% endblock %} |
|
25 | ||
26 |
{% block content %} |
|
27 |
<div class="section"> |
|
28 |
<h3>{% trans 'Unavailabilities' %}</h3> |
|
29 |
<div> |
|
30 |
{% block agenda-settings %} |
|
31 |
{% if unavailability_calendar.timeperiodexception_set.count %} |
|
32 |
<ul class="objects-list single-links"> |
|
33 |
{% for time_period_exception in unavailability_calendar.timeperiodexception_set.all %} |
|
34 |
<li><a rel="popup" href="{% url 'chrono-manager-time-period-exception-edit' pk=time_period_exception.id %}">{{ time_period_exception }}</a> |
|
35 |
<a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-exception-delete' pk=time_period_exception.id %}">{% trans "remove" %}</a> |
|
36 |
</li> |
|
37 |
{% endfor %} |
|
38 |
</ul> |
|
39 |
{% else %} |
|
40 |
<div class="big-msg-info"> |
|
41 |
{% blocktrans %} |
|
42 |
There is no unavailabilities yet. Click on the "Add Unavailabilty" button in |
|
43 |
the top right of the page to add a first one. |
|
44 |
{% endblocktrans %} |
|
45 |
</div> |
|
46 |
{% endif %} |
|
47 |
</div> |
|
48 |
</div> |
|
49 |
{% endblock %} |
|
50 | ||
51 | ||
52 |
{% 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'), |
... | ... | |
158 | 193 |
url(r'^agendas/(?P<pk>\d+)/add-desk$', views.agenda_add_desk, name='chrono-manager-agenda-add-desk'), |
159 | 194 |
url(r'^desks/(?P<pk>\d+)/edit$', views.desk_edit, name='chrono-manager-desk-edit'), |
160 | 195 |
url(r'^desks/(?P<pk>\d+)/delete$', views.desk_delete, name='chrono-manager-desk-delete'), |
196 |
url( |
|
197 |
r'^desk/(?P<pk>\d+)/unavailability-calendar/(?P<unavailability_calendar_pk>\d+)/toogle/$', |
|
198 |
views.unavailability_calendar_toggle_view, |
|
199 |
name='chrono-manager-unavailability-calendar-toggle-view', |
|
200 |
), |
|
161 | 201 |
url( |
162 | 202 |
r'^agendas/(?P<agenda_pk>\d+)/desk/(?P<pk>\d+)/add-time-period-exception$', |
163 | 203 |
views.agenda_add_time_period_exception, |
chrono/manager/views.py | ||
---|---|---|
23 | 23 | |
24 | 24 |
from django.contrib import messages |
25 | 25 |
from django.core.exceptions import PermissionDenied |
26 |
from django.db.models import Q |
|
26 |
from django.db.models import Q, Value, BooleanField
|
|
27 | 27 |
from django.db.models import Min, Max |
28 | 28 |
from django.http import Http404, HttpResponse, HttpResponseRedirect |
29 | 29 |
from django.shortcuts import get_object_or_404 |
... | ... | |
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, |
|
97 | 100 |
) |
98 | 101 |
from .utils import import_site |
99 | 102 | |
... | ... | |
1237 | 1240 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id}) |
1238 | 1241 | |
1239 | 1242 | |
1243 |
class ManagedTimePeriodExceptionMixin(object): |
|
1244 | ||
1245 |
desk = None |
|
1246 |
unavailability_calendar = None |
|
1247 | ||
1248 |
def dispatch(self, request, *args, **kwargs): |
|
1249 |
object_ = self.get_object() |
|
1250 |
if object_.desk: |
|
1251 |
self.desk = self.get_object().desk |
|
1252 |
if not self.desk.agenda.can_be_managed(request.user): |
|
1253 |
raise PermissionDenied() |
|
1254 |
elif object_.unavailability_calendar: |
|
1255 |
self.unavailability_calendar = object_.unavailability_calendar |
|
1256 |
if not self.unavailability_calendar.can_be_managed(request.user): |
|
1257 |
raise PermissionDenied() |
|
1258 |
return super().dispatch(request, *args, **kwargs) |
|
1259 | ||
1260 |
def get_context_data(self, **kwargs): |
|
1261 |
context = super().get_context_data(**kwargs) |
|
1262 |
if self.desk: |
|
1263 |
context['desk'] = self.object.desk |
|
1264 |
context['agenda'] = self.object.desk.agenda |
|
1265 |
return context |
|
1266 | ||
1267 |
def get_success_url(self): |
|
1268 |
if self.desk: |
|
1269 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id}) |
|
1270 |
elif self.unavailability_calendar: |
|
1271 |
return reverse( |
|
1272 |
'chrono-manager-unavailability-calendar-settings', |
|
1273 |
kwargs={'pk': self.unavailability_calendar.pk}, |
|
1274 |
) |
|
1275 | ||
1276 | ||
1240 | 1277 |
class AgendaSettings(ManagedAgendaMixin, DetailView): |
1241 | 1278 |
model = Agenda |
1242 | 1279 | |
... | ... | |
1258 | 1295 |
] |
1259 | 1296 |
if self.agenda.kind == 'meetings': |
1260 | 1297 |
context['has_resources'] = Resource.objects.exists() |
1298 |
context['has_unavailability_calendars'] = UnavailabilityCalendar.objects.exists() |
|
1261 | 1299 |
return context |
1262 | 1300 | |
1263 | 1301 |
def get_events(self): |
... | ... | |
1538 | 1576 |
agenda_delete_resource = AgendaResourceDeleteView.as_view() |
1539 | 1577 | |
1540 | 1578 | |
1579 |
class UnavailabilityCalendarToggleView(ManagedDeskMixin, DetailView): |
|
1580 |
model = UnavailabilityCalendar |
|
1581 |
pk_url_kwarg = 'unavailability_calendar_pk' |
|
1582 | ||
1583 |
def get(self, request, *args, **kwargs): |
|
1584 |
unavailability_calendar = self.get_object() |
|
1585 |
try: |
|
1586 |
self.desk.unavailability_calendars.get(pk=unavailability_calendar.pk) |
|
1587 |
self.desk.unavailability_calendars.remove(unavailability_calendar) |
|
1588 |
message = _( |
|
1589 |
'Unavailability calendar %(unavailability_calendar)s has been disabled on desk %(desk)s.' |
|
1590 |
) |
|
1591 | ||
1592 |
except UnavailabilityCalendar.DoesNotExist: |
|
1593 |
self.desk.unavailability_calendars.add(unavailability_calendar) |
|
1594 |
message = _( |
|
1595 |
'Unavailability calendar %(unavailability_calendar)s has been enabled on desk %(desk)s.' |
|
1596 |
) |
|
1597 |
messages.info( |
|
1598 |
self.request, message % {'unavailability_calendar': unavailability_calendar, 'desk': self.desk} |
|
1599 |
) |
|
1600 |
return HttpResponseRedirect( |
|
1601 |
reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda_id}) |
|
1602 |
) |
|
1603 | ||
1604 | ||
1605 |
unavailability_calendar_toggle_view = UnavailabilityCalendarToggleView.as_view() |
|
1606 | ||
1607 | ||
1541 | 1608 |
class AgendaAddMeetingTypeView(ManagedAgendaMixin, CreateView): |
1542 | 1609 |
template_name = 'chrono/manager_meeting_type_form.html' |
1543 | 1610 |
model = MeetingType |
... | ... | |
1745 | 1812 |
model = TimePeriodException |
1746 | 1813 |
form_class = TimePeriodExceptionForm |
1747 | 1814 | |
1815 |
def get_context_data(self, **kwargs): |
|
1816 |
context = super().get_context_data(**kwargs) |
|
1817 |
context['cancel_url'] = reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.pk}) |
|
1818 |
return context |
|
1819 | ||
1748 | 1820 |
def form_valid(self, form): |
1749 | 1821 |
result = super().form_valid(form) |
1750 | 1822 |
exceptions = [self.object] |
... | ... | |
1775 | 1847 |
agenda_add_time_period_exception = AgendaAddTimePeriodExceptionView.as_view() |
1776 | 1848 | |
1777 | 1849 | |
1778 |
class TimePeriodExceptionEditView(ManagedDeskSubobjectMixin, UpdateView):
|
|
1850 |
class TimePeriodExceptionEditView(ManagedTimePeriodExceptionMixin, UpdateView):
|
|
1779 | 1851 |
template_name = 'chrono/manager_time_period_exception_form.html' |
1780 | 1852 |
model = TimePeriodException |
1781 | 1853 |
form_class = TimePeriodExceptionForm |
1782 | 1854 | |
1855 |
def get_context_data(self): |
|
1856 |
context = super().get_context_data() |
|
1857 |
if self.desk: |
|
1858 |
context['cancel_url'] = reverse( |
|
1859 |
'chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.pk} |
|
1860 |
) |
|
1861 |
elif self.unavailability_calendar: |
|
1862 |
context['cancel_url'] = reverse( |
|
1863 |
'chrono-manager-unavailability-calendar-settings', |
|
1864 |
kwargs={'pk': self.unavailability_calendar.pk}, |
|
1865 |
) |
|
1866 |
return context |
|
1867 | ||
1783 | 1868 | |
1784 | 1869 |
time_period_exception_edit = TimePeriodExceptionEditView.as_view() |
1785 | 1870 | |
... | ... | |
1812 | 1897 |
time_period_exception_extract_list = TimePeriodExceptionExtractListView.as_view() |
1813 | 1898 | |
1814 | 1899 | |
1815 |
class TimePeriodExceptionDeleteView(ManagedDeskSubobjectMixin, DeleteView):
|
|
1900 |
class TimePeriodExceptionDeleteView(ManagedTimePeriodExceptionMixin, DeleteView):
|
|
1816 | 1901 |
template_name = 'chrono/manager_confirm_exception_delete.html' |
1817 | 1902 |
model = TimePeriodException |
1818 | 1903 | |
1819 | 1904 |
def get_success_url(self): |
1820 |
referer = self.request.META.get('HTTP_REFERER') |
|
1821 |
success_url = reverse('chrono-manager-time-period-exception-list', kwargs={'pk': self.desk.pk}) |
|
1822 |
if success_url in referer: |
|
1823 |
return success_url |
|
1905 |
if self.desk: |
|
1906 |
referer = self.request.META.get('HTTP_REFERER') |
|
1907 |
success_url = reverse('chrono-manager-time-period-exception-list', kwargs={'pk': self.desk.pk}) |
|
1908 |
if success_url in referer: |
|
1909 |
return success_url |
|
1824 | 1910 | |
1825 | 1911 |
success_url = super(TimePeriodExceptionDeleteView, self).get_success_url() |
1826 |
if 'from_popup' in self.request.GET: |
|
1912 |
if self.desk and 'from_popup' in self.request.GET:
|
|
1827 | 1913 |
success_url = '{}?display_exceptions={}'.format(success_url, self.desk.pk) |
1828 | 1914 |
return success_url |
1829 | 1915 | |
... | ... | |
1841 | 1927 | |
1842 | 1928 |
def get_context_data(self, **kwargs): |
1843 | 1929 |
context = super(DeskImportTimePeriodExceptionsView, self).get_context_data(**kwargs) |
1844 |
context['exception_sources'] = self.get_object().timeperiodexceptionsource_set.all() |
|
1930 |
desk = self.get_object() |
|
1931 |
context['exception_sources'] = desk.timeperiodexceptionsource_set.all() |
|
1932 |
context['desk'] = desk |
|
1933 |
context['unavailability_calendars'] = ( |
|
1934 |
UnavailabilityCalendar.objects.filter(desks=desk) |
|
1935 |
.annotate(enabled=Value(True, BooleanField())) |
|
1936 |
.union( |
|
1937 |
UnavailabilityCalendar.objects.exclude(desks=desk).annotate( |
|
1938 |
enabled=Value(False, BooleanField()) |
|
1939 |
) |
|
1940 |
) |
|
1941 |
) |
|
1845 | 1942 |
return context |
1846 | 1943 | |
1847 | 1944 |
def form_valid(self, form): |
... | ... | |
2082 | 2179 |
time_period_exception_source_toggle = TimePeriodExceptionSourceToggleView.as_view() |
2083 | 2180 | |
2084 | 2181 | |
2182 |
class ViewableUnavailabilityCalendarMixin(object): |
|
2183 |
unavailability_calendar = None |
|
2184 | ||
2185 |
def set_unavailability_calendar(self, **kwargs): |
|
2186 |
self.unavailability_calendar = get_object_or_404(UnavailabilityCalendar, id=kwargs.get('pk')) |
|
2187 | ||
2188 |
def dispatch(self, request, *args, **kwargs): |
|
2189 |
self.set_unavailability_calendar(**kwargs) |
|
2190 |
if not self.check_permissions(request.user): |
|
2191 |
raise PermissionDenied() |
|
2192 |
return super().dispatch(request, *args, **kwargs) |
|
2193 | ||
2194 |
def check_permissions(self, user): |
|
2195 |
return self.unavailability_calendar.can_be_viewed(user) |
|
2196 | ||
2197 |
def get_context_data(self, **kwargs): |
|
2198 |
context = super().get_context_data(**kwargs) |
|
2199 |
context['unavailability_calendar'] = self.unavailability_calendar |
|
2200 |
context['user_can_manage'] = self.unavailability_calendar.can_be_managed(self.request.user) |
|
2201 |
return context |
|
2202 | ||
2203 | ||
2204 |
class ManagedUnavailabilityCalendarMixin(ViewableUnavailabilityCalendarMixin): |
|
2205 |
def check_permissions(self, user): |
|
2206 |
return self.unavailability_calendar.can_be_managed(user) |
|
2207 | ||
2208 |
def get_success_url(self): |
|
2209 |
return reverse( |
|
2210 |
'chrono-manager-unavailability-calendar-settings', kwargs={'pk': self.unavailability_calendar.id} |
|
2211 |
) |
|
2212 | ||
2213 | ||
2214 |
class UnavailabilityCalendarListView(ListView): |
|
2215 |
template_name = 'chrono/manager_unavailability_calendar_list.html' |
|
2216 |
model = UnavailabilityCalendar |
|
2217 | ||
2218 |
def get_queryset(self): |
|
2219 |
queryset = super().get_queryset() |
|
2220 |
if not self.request.user.is_staff: |
|
2221 |
group_ids = [x.id for x in self.request.user.groups.all()] |
|
2222 |
queryset = queryset.filter(Q(view_role_id__in=group_ids) | Q(edit_role_id__in=group_ids)) |
|
2223 |
return queryset.order_by('label') |
|
2224 | ||
2225 | ||
2226 |
unavailability_calendar_list = UnavailabilityCalendarListView.as_view() |
|
2227 | ||
2228 | ||
2229 |
class UnavailabilityCalendarAddView(CreateView): |
|
2230 |
template_name = 'chrono/manager_unavailability_calendar_form.html' |
|
2231 |
model = UnavailabilityCalendar |
|
2232 |
form_class = UnavailabilityCalendarAddForm |
|
2233 | ||
2234 |
def dispatch(self, request, *args, **kwargs): |
|
2235 |
if not request.user.is_staff: |
|
2236 |
raise PermissionDenied() |
|
2237 |
return super().dispatch(request, *args, **kwargs) |
|
2238 | ||
2239 |
def get_success_url(self): |
|
2240 |
return reverse('chrono-manager-unavailability-calendar-view', kwargs={'pk': self.object.id}) |
|
2241 | ||
2242 | ||
2243 |
unavailability_calendar_add = UnavailabilityCalendarAddView.as_view() |
|
2244 | ||
2245 | ||
2246 |
class UnavailabilityCalendarDetailView(ViewableUnavailabilityCalendarMixin, DetailView): |
|
2247 |
template_name = 'chrono/manager_unavailability_calendar_detail.html' |
|
2248 |
model = UnavailabilityCalendar |
|
2249 | ||
2250 |
def get_context_data(self, **kwargs): |
|
2251 |
context = super().get_context_data(**kwargs) |
|
2252 |
context['agendas'] = Agenda.objects.filter( |
|
2253 |
desk__unavailability_calendars__pk=self.unavailability_calendar.pk |
|
2254 |
).distinct() |
|
2255 |
return context |
|
2256 | ||
2257 | ||
2258 |
unavailability_calendar_view = UnavailabilityCalendarDetailView.as_view() |
|
2259 | ||
2260 | ||
2261 |
class UnavailabilityCalendarEditView(ManagedUnavailabilityCalendarMixin, UpdateView): |
|
2262 |
template_name = 'chrono/manager_unavailability_calendar_form.html' |
|
2263 |
model = UnavailabilityCalendar |
|
2264 |
form_class = UnavailabilityCalendarEditForm |
|
2265 | ||
2266 | ||
2267 |
unavailability_calendar_edit = UnavailabilityCalendarEditView.as_view() |
|
2268 | ||
2269 | ||
2270 |
class UnavailabilityCalendarDeleteView(DeleteView): |
|
2271 |
template_name = 'chrono/manager_confirm_delete.html' |
|
2272 |
model = UnavailabilityCalendar |
|
2273 | ||
2274 |
def dispatch(self, request, *args, **kwargs): |
|
2275 |
if not request.user.is_staff: |
|
2276 |
raise PermissionDenied() |
|
2277 |
return super().dispatch(request, *args, **kwargs) |
|
2278 | ||
2279 |
def get_success_url(self): |
|
2280 |
return reverse('chrono-manager-unavailability-calendar-list') |
|
2281 | ||
2282 | ||
2283 |
unavailability_calendar_delete = UnavailabilityCalendarDeleteView.as_view() |
|
2284 | ||
2285 | ||
2286 |
class UnavailabilityCalendarSettings(ManagedUnavailabilityCalendarMixin, DetailView): |
|
2287 |
template_name = 'chrono/manager_unavailability_calendar_settings.html' |
|
2288 |
model = UnavailabilityCalendar |
|
2289 | ||
2290 |
def get_context_data(self, **kwargs): |
|
2291 |
context = super(UnavailabilityCalendarSettings, self).get_context_data(**kwargs) |
|
2292 |
context['unavailability_calendar'] = self.object |
|
2293 |
return context |
|
2294 | ||
2295 | ||
2296 |
unavailability_calendar_settings = UnavailabilityCalendarSettings.as_view() |
|
2297 | ||
2298 | ||
2299 |
class UnavailabilityCalendarAddUnavailabilityView(ManagedUnavailabilityCalendarMixin, CreateView): |
|
2300 |
template_name = 'chrono/manager_time_period_exception_form.html' |
|
2301 |
form_class = TimePeriodExceptionForm |
|
2302 |
model = TimePeriodException |
|
2303 | ||
2304 |
def get_form_kwargs(self): |
|
2305 |
kwargs = super().get_form_kwargs() |
|
2306 |
if not kwargs.get('instance'): |
|
2307 |
kwargs['instance'] = self.model() |
|
2308 |
kwargs['instance'].unavailability_calendar = self.unavailability_calendar |
|
2309 |
return kwargs |
|
2310 | ||
2311 |
def get_context_data(self, **kwargs): |
|
2312 |
context = super(UnavailabilityCalendarAddUnavailabilityView, self).get_context_data(**kwargs) |
|
2313 |
context['cancel_url'] = reverse( |
|
2314 |
'chrono-manager-unavailability-calendar-settings', kwargs={'pk': self.unavailability_calendar.pk,} |
|
2315 |
) |
|
2316 |
return context |
|
2317 | ||
2318 | ||
2319 |
unavailability_calendar_add_unavailability = UnavailabilityCalendarAddUnavailabilityView.as_view() |
|
2320 | ||
2321 | ||
2085 | 2322 |
def menu_json(request): |
2086 | 2323 |
label = _('Agendas') |
2087 | 2324 |
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 |
desk = meetings_agenda.desk_set.first() |
|
4136 |
datetimes_url = '/api/agenda/%s/meetings/%s/datetimes/' % (meetings_agenda.slug, meeting_type.slug) |
|
4137 |
resp = app.get(datetimes_url) |
|
4138 |
assert len(resp.json['data']) == 144 |
|
4139 | ||
4140 |
# create an unvalailability calendar |
|
4141 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='foo holydays') |
|
4142 |
TimePeriodException.objects.create( |
|
4143 |
unavailability_calendar=unavailability_calendar, |
|
4144 |
start_datetime=make_aware(datetime.datetime(2017, 5, 22, 10, 0)), |
|
4145 |
end_datetime=make_aware(datetime.datetime(2017, 5, 22, 11, 0)), |
|
4146 |
) |
|
4147 |
unavailability_calendar.desks.add(desk) |
|
4148 | ||
4149 |
# 2 slots are gone |
|
4150 |
resp2 = app.get(datetimes_url) |
|
4151 |
assert len(resp.json['data']) == len(resp2.json['data']) + 2 |
|
4152 | ||
4153 |
# add a standard desk exception |
|
4154 |
desk = meetings_agenda.desk_set.first() |
|
4155 |
TimePeriodException.objects.create( |
|
4156 |
desk=desk, |
|
4157 |
start_datetime=make_aware(datetime.datetime(2017, 5, 22, 11, 0)), |
|
4158 |
end_datetime=make_aware(datetime.datetime(2017, 5, 22, 12, 0)), |
|
4159 |
) |
|
4160 |
# 4 slots are gone |
|
4161 |
resp3 = app.get(datetimes_url) |
|
4162 |
assert len(resp.json['data']) == len(resp3.json['data']) + 4 |
|
4163 | ||
4164 | ||
4165 |
def test_unavailabilitycalendar_on_virtual_datetimes(app, user, mock_now): |
|
4166 |
foo_agenda = Agenda.objects.create(label='Foo Meeting', kind='meetings', maximal_booking_delay=7) |
|
4167 |
MeetingType.objects.create(agenda=foo_agenda, label='Meeting Type', duration=30) |
|
4168 |
foo_desk_1 = Desk.objects.create(agenda=foo_agenda, label='Foo desk 1') |
|
4169 |
TimePeriod.objects.create( |
|
4170 |
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1, |
|
4171 |
) |
|
4172 |
TimePeriod.objects.create( |
|
4173 |
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=foo_desk_1, |
|
4174 |
) |
|
4175 |
virt_agenda = Agenda.objects.create(label='Virtual Agenda', kind='virtual') |
|
4176 |
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda) |
|
4177 | ||
4178 |
api_url = '/api/agenda/%s/meetings/meeting-type/datetimes/' % (virt_agenda.slug) |
|
4179 |
resp = app.get(api_url) |
|
4180 |
# 8 slots |
|
4181 |
data = resp.json['data'] |
|
4182 |
assert len(data) == 8 |
|
4183 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
4184 |
assert data[1]['datetime'] == '2017-05-22 10:30:00' |
|
4185 |
assert data[2]['datetime'] == '2017-05-22 11:00:00' |
|
4186 | ||
4187 |
# exclude one hour the first day through an unvalailability calendar on the foo agenda |
|
4188 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='foo holydays') |
|
4189 |
TimePeriodException.objects.create( |
|
4190 |
unavailability_calendar=unavailability_calendar, |
|
4191 |
start_datetime=make_aware(datetime.datetime(2017, 5, 22, 11, 0)), |
|
4192 |
end_datetime=make_aware(datetime.datetime(2017, 5, 22, 12, 0)), |
|
4193 |
) |
|
4194 |
unavailability_calendar.desks.add(foo_desk_1) |
|
4195 | ||
4196 |
resp = app.get(api_url) |
|
4197 |
data = resp.json['data'] |
|
4198 |
assert len(data) == 6 |
|
4199 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
4200 |
assert data[1]['datetime'] == '2017-05-22 10:30:00' |
|
4201 |
# no more slots the 22 thanks to the unavailability calendar |
|
4202 |
assert data[2]['datetime'] == '2017-05-23 10:00:00' |
|
4203 | ||
4204 |
# exclude the second day |
|
4205 |
TimePeriodException.objects.create( |
|
4206 |
unavailability_calendar=unavailability_calendar, |
|
4207 |
start_datetime=make_aware(datetime.datetime(2017, 5, 23, 9, 0)), |
|
4208 |
end_datetime=make_aware(datetime.datetime(2017, 5, 23, 18, 0)), |
|
4209 |
) |
|
4210 |
resp = app.get(api_url) |
|
4211 |
data = resp.json['data'] |
|
4212 |
assert len(data) == 2 |
|
4213 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
4214 |
assert data[1]['datetime'] == '2017-05-22 10:30:00' |
|
4215 | ||
4216 |
# add a second real agenda |
|
4217 |
bar_agenda = Agenda.objects.create(label='Bar Meeting', kind='meetings', maximal_booking_delay=7) |
|
4218 |
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=bar_agenda) |
|
4219 |
MeetingType.objects.create(agenda=bar_agenda, label='Meeting Type', duration=30) |
|
4220 |
bar_desk_1 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 1') |
|
4221 |
bar_desk_2 = Desk.objects.create(agenda=bar_agenda, label='Bar desk 2') |
|
4222 |
TimePeriod.objects.create( |
|
4223 |
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1, |
|
4224 |
) |
|
4225 |
TimePeriod.objects.create( |
|
4226 |
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_1, |
|
4227 |
) |
|
4228 |
TimePeriod.objects.create( |
|
4229 |
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_2, |
|
4230 |
) |
|
4231 |
TimePeriod.objects.create( |
|
4232 |
weekday=1, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=bar_desk_2, |
|
4233 |
) |
|
4234 | ||
4235 |
# bar_agenda has the same time periods than foo_agenda, but no unavailability calendar |
|
4236 |
# so we are back at the start : 8 slots |
|
4237 |
resp = app.get(api_url) |
|
4238 |
data = resp.json['data'] |
|
4239 |
assert len(data) == 8 |
|
4240 | ||
4241 |
# exclude one hour the second day through another unvalailability calendar on the bar agenda |
|
4242 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='bar holydays') |
|
4243 |
TimePeriodException.objects.create( |
|
4244 |
unavailability_calendar=unavailability_calendar, |
|
4245 |
start_datetime=make_aware(datetime.datetime(2017, 5, 23, 11, 0)), |
|
4246 |
end_datetime=make_aware(datetime.datetime(2017, 5, 23, 12, 0)), |
|
4247 |
) |
|
4248 |
unavailability_calendar.desks.add(bar_desk_1, bar_desk_2) |
|
4249 | ||
4250 |
# 2 slots are gone |
|
4251 |
resp = app.get(api_url) |
|
4252 |
data = resp.json['data'] |
|
4253 |
assert len(data) == 6 |
|
4254 |
assert data[0]['datetime'] == '2017-05-22 10:00:00' |
|
4255 |
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): |
... | ... | |
4376 | 4377 |
'Reminder: you have a booking for event "{{ event_label }}", on 02/06 at 2:30 p.m.. Take ID card.' |
4377 | 4378 |
in resp.text |
4378 | 4379 |
) |
4380 | ||
4381 | ||
4382 |
def test_no_unavailability_calendar(app, admin_user): |
|
4383 |
app = login(app) |
|
4384 | ||
4385 |
# empty unavailability calendars list |
|
4386 |
resp = app.get('/manage/') |
|
4387 |
resp = resp.click('Unavailability calendars') |
|
4388 |
assert "This site doesn't have any unavailability calendar yet" in resp.text |
|
4389 | ||
4390 |
# on the agenda settings page, no unavailability calendar reference |
|
4391 |
agenda = Agenda.objects.create(label='Agenda', kind='meetings') |
|
4392 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
4393 |
assert 'unavailability calendar' not in resp.text |
|
4394 | ||
4395 | ||
4396 |
def test_add_unavailability_calendar(app, admin_user): |
|
4397 |
app = login(app) |
|
4398 |
resp = app.get('/manage/') |
|
4399 |
resp = resp.click('Unavailability calendars') |
|
4400 |
resp = resp.click('New') |
|
4401 |
resp.form['label'] = 'Foo bar' |
|
4402 |
resp = resp.form.submit() |
|
4403 |
unavailability_calendar = UnavailabilityCalendar.objects.latest('pk') |
|
4404 |
assert resp.location.endswith('/manage/unavailability-calendar/%s/' % unavailability_calendar.pk) |
|
4405 |
assert unavailability_calendar.label == 'Foo bar' |
|
4406 |
assert unavailability_calendar.slug == 'foo-bar' |
|
4407 |
resp = resp.follow() |
|
4408 |
assert 'This unavailability calendar is not used yet.' in resp.text |
|
4409 |
resp = app.get('/manage/unavailability-calendars/') |
|
4410 |
assert 'Foo bar' in resp.text |
|
4411 |
assert 'foo-bar' in resp.text |
|
4412 | ||
4413 | ||
4414 |
def test_used_unavailability_calendar(app, admin_user): |
|
4415 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
4416 |
agenda = Agenda.objects.create(label='Foo', kind='meetings') |
|
4417 |
desk = Desk.objects.create(agenda=agenda, label='desk') |
|
4418 | ||
4419 |
app = login(app) |
|
4420 |
url = '/manage/unavailability-calendar/%s/' % unavailability_calendar.pk |
|
4421 |
resp = app.get(url) |
|
4422 |
assert 'This unavailability calendar is not used yet.' in resp.text |
|
4423 |
assert 'Foo' not in resp.text |
|
4424 | ||
4425 |
desk.unavailability_calendars.add(unavailability_calendar) |
|
4426 |
resp = app.get(url) |
|
4427 |
assert 'This unavailability calendar is not used yet.' not in resp.text |
|
4428 |
assert 'Foo' in resp.text |
|
4429 | ||
4430 | ||
4431 |
def test_edit_unavailability_calendar(app, admin_user): |
|
4432 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4433 |
app = login(app) |
|
4434 |
settings_url = '/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk |
|
4435 |
resp = app.get(settings_url) |
|
4436 |
resp = resp.click('Options') |
|
4437 |
resp.form['label'] = 'Bar' |
|
4438 |
resp = resp.form.submit() |
|
4439 |
assert resp.location.endswith(settings_url) |
|
4440 |
resp = resp.follow() |
|
4441 |
assert 'Bar' in resp.text |
|
4442 |
unavailability_calendar.refresh_from_db() |
|
4443 |
assert unavailability_calendar.label == 'Bar' |
|
4444 | ||
4445 | ||
4446 |
def test_delete_unavailability_calendar(app, admin_user): |
|
4447 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4448 |
app = login(app) |
|
4449 |
resp = app.get('/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk) |
|
4450 |
resp = resp.click('Delete') |
|
4451 |
resp = resp.form.submit() |
|
4452 |
assert resp.location.endswith('/manage/unavailability-calendars/') |
|
4453 |
resp = resp.follow() |
|
4454 |
assert 'Foo' not in resp.text |
|
4455 |
assert UnavailabilityCalendar.objects.count() == 0 |
|
4456 | ||
4457 | ||
4458 |
def test_unavailability_calendar_add_time_period_exeptions(app, admin_user): |
|
4459 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4460 |
app = login(app) |
|
4461 |
resp = app.get('/manage/unavailability-calendar/%s/' % unavailability_calendar.pk) |
|
4462 |
resp = resp.click('Settings') |
|
4463 |
assert 'There is no unavailabilities yet' in resp.text |
|
4464 |
resp = resp.click('Add Unavailability') |
|
4465 |
assert 'all_desks' not in resp.form.fields |
|
4466 |
today = datetime.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0) |
|
4467 |
tomorrow = make_aware(today + datetime.timedelta(days=1)) |
|
4468 |
resp.form['label'] = 'Exception 1' |
|
4469 |
resp.form['start_datetime_0'] = tomorrow.strftime('%Y-%m-%d') |
|
4470 |
resp.form['start_datetime_1'] = '08:00' |
|
4471 |
resp.form['end_datetime_0'] = tomorrow.strftime('%Y-%m-%d') |
|
4472 |
resp.form['end_datetime_1'] = '16:00' |
|
4473 |
resp = resp.form.submit().follow() |
|
4474 |
assert 'Exception 1' in resp.text |
|
4475 | ||
4476 |
time_period_exception = TimePeriodException.objects.first() |
|
4477 |
assert time_period_exception.unavailability_calendar == unavailability_calendar |
|
4478 |
assert time_period_exception.desk is None |
|
4479 |
assert time_period_exception.label == 'Exception 1' |
|
4480 | ||
4481 | ||
4482 |
def test_unavailability_calendar_edit_time_period_exeptions(app, admin_user): |
|
4483 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4484 |
time_period_exception = TimePeriodException.objects.create( |
|
4485 |
unavailability_calendar=unavailability_calendar, |
|
4486 |
label='Exception 1', |
|
4487 |
start_datetime=now() - datetime.timedelta(days=2), |
|
4488 |
end_datetime=now() - datetime.timedelta(days=1), |
|
4489 |
) |
|
4490 | ||
4491 |
app = login(app) |
|
4492 |
resp = app.get('/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk) |
|
4493 |
assert 'Exception 1' in resp.text |
|
4494 |
resp = resp.click(href='/manage/time-period-exceptions/%s/edit' % time_period_exception.pk) |
|
4495 |
assert 'all_desks' not in resp.form.fields |
|
4496 |
resp.form['label'] = 'Exception foo' |
|
4497 |
resp = resp.form.submit().follow() |
|
4498 |
assert 'Exception foo' in resp.text |
|
4499 |
time_period_exception.refresh_from_db() |
|
4500 |
assert 'Exception foo' == time_period_exception.label |
|
4501 | ||
4502 | ||
4503 |
def test_unavailability_calendar_delete_time_period_exeptions(app, admin_user): |
|
4504 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Foo') |
|
4505 |
time_period_exception = TimePeriodException.objects.create( |
|
4506 |
unavailability_calendar=unavailability_calendar, |
|
4507 |
label='Exception 1', |
|
4508 |
start_datetime=now() - datetime.timedelta(days=2), |
|
4509 |
end_datetime=now() - datetime.timedelta(days=1), |
|
4510 |
) |
|
4511 | ||
4512 |
app = login(app) |
|
4513 |
resp = app.get('/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk) |
|
4514 |
assert 'Exception 1' in resp.text |
|
4515 |
resp = resp.click(href='/manage/time-period-exceptions/%s/delete' % time_period_exception.pk) |
|
4516 |
resp = resp.form.submit().follow() |
|
4517 |
assert 'Exception foo' not in resp.text |
|
4518 |
assert unavailability_calendar.timeperiodexception_set.count() == 0 |
|
4519 | ||
4520 | ||
4521 |
def test_activate_unavailability_calendar_in_desk(app, admin_user): |
|
4522 |
app = login(app) |
|
4523 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
4524 |
agenda = Agenda.objects.create(label='Agenda', kind='meetings') |
|
4525 |
desk = Desk.objects.create(agenda=agenda, label='desk') |
|
4526 |
exceptions_url = '/manage/agendas/desk/%s/import-exceptions-from-ics/' % desk.pk |
|
4527 |
resp = app.get(exceptions_url) |
|
4528 |
assert 'calendar' in resp.text |
|
4529 |
assert 'enable' in resp.text |
|
4530 |
resp = resp.click( |
|
4531 |
href='/manage/desk/%s/unavailability-calendar/%s/toogle/' % (desk.pk, unavailability_calendar.pk) |
|
4532 |
) |
|
4533 |
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk) |
|
4534 |
resp = app.get(exceptions_url) |
|
4535 |
assert 'calendar' in resp.text |
|
4536 |
assert 'disable' in resp.text |
|
4537 |
assert desk.unavailability_calendars.get(pk=unavailability_calendar.pk) |
|
4538 | ||
4539 | ||
4540 |
def test_deactivate_unavailability_calendar_in_desk(app, admin_user): |
|
4541 |
app = login(app) |
|
4542 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
4543 |
agenda = Agenda.objects.create(label='Agenda', kind='meetings') |
|
4544 |
desk = Desk.objects.create(agenda=agenda, label='desk') |
|
4545 |
desk.unavailability_calendars.add(unavailability_calendar) |
|
4546 |
exceptions_url = '/manage/agendas/desk/%s/import-exceptions-from-ics/' % desk.pk |
|
4547 |
resp = app.get(exceptions_url) |
|
4548 |
assert 'calendar' in resp.text |
|
4549 |
assert 'disable' in resp.text |
|
4550 |
resp = resp.click( |
|
4551 |
href='/manage/desk/%s/unavailability-calendar/%s/toogle/' % (desk.pk, unavailability_calendar.pk) |
|
4552 |
) |
|
4553 |
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.pk) |
|
4554 |
resp = app.get(exceptions_url) |
|
4555 |
assert 'calendar' in resp.text |
|
4556 |
assert 'enable' in resp.text |
|
4557 |
assert desk.unavailability_calendars.count() == 0 |
|
4558 | ||
4559 | ||
4560 |
def test_unavailability_calendar_homepage_permission(app, manager_user): |
|
4561 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4562 |
app = login(app, username='manager', password='manager') |
|
4563 |
resp = app.get('/manage/', status=403) |
|
4564 |
group = manager_user.groups.all()[0] |
|
4565 |
unavailability_calendar.view_role = group |
|
4566 |
unavailability_calendar.edit_role = None |
|
4567 |
unavailability_calendar.save() |
|
4568 |
resp = app.get('/manage/') |
|
4569 |
resp = resp.click('Unavailability calendars') |
|
4570 |
unavailability_calendar.view_role = None |
|
4571 |
unavailability_calendar.edit_role = group |
|
4572 |
unavailability_calendar.save() |
|
4573 |
resp = app.get('/manage/') |
|
4574 |
resp = resp.click('Unavailability calendars') |
|
4575 | ||
4576 | ||
4577 |
def test_unavailability_calendar_list_permissions(app, manager_user): |
|
4578 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4579 |
app = login(app, username='manager', password='manager') |
|
4580 |
app.get('/manage/unavailability-calendars/', status=403) |
|
4581 |
group = manager_user.groups.all()[0] |
|
4582 |
unavailability_calendar.view_role = group |
|
4583 |
unavailability_calendar.edit_role = None |
|
4584 |
unavailability_calendar.save() |
|
4585 |
resp = app.get('/manage/unavailability-calendars/') |
|
4586 |
assert 'Calendar 1' in resp.text |
|
4587 |
assert 'New' not in resp.text |
|
4588 |
unavailability_calendar.view_role = None |
|
4589 |
unavailability_calendar.edit_role = group |
|
4590 |
unavailability_calendar.save() |
|
4591 |
assert 'Calendar 1' in resp.text |
|
4592 |
assert 'New' not in resp.text |
|
4593 | ||
4594 | ||
4595 |
def test_unavailability_calendar_add_permissions(app, manager_user): |
|
4596 |
app = login(app, username='manager', password='manager') |
|
4597 |
url = '/manage/unavailability-calendar/add/' |
|
4598 |
app.get(url, status=403) |
|
4599 | ||
4600 | ||
4601 |
def test_unavailability_calendar_detail_permissions(app, manager_user): |
|
4602 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4603 |
app = login(app, username='manager', password='manager') |
|
4604 |
detail_url = '/manage/unavailability-calendar/%s/' % unavailability_calendar.pk |
|
4605 |
resp = app.get(detail_url, status=403) |
|
4606 |
group = manager_user.groups.all()[0] |
|
4607 |
unavailability_calendar.view_role = group |
|
4608 |
unavailability_calendar.edit_role = None |
|
4609 |
unavailability_calendar.save() |
|
4610 |
resp = app.get(detail_url) |
|
4611 |
assert 'Settings' not in resp.text |
|
4612 |
unavailability_calendar.view_role = None |
|
4613 |
unavailability_calendar.edit_role = group |
|
4614 |
unavailability_calendar.save() |
|
4615 |
resp = app.get(detail_url) |
|
4616 |
assert 'Settings' in resp.text |
|
4617 | ||
4618 | ||
4619 |
def test_unavailability_calendar_edit_permissions(app, manager_user): |
|
4620 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4621 |
app = login(app, username='manager', password='manager') |
|
4622 |
url = '/manage/unavailability-calendar/%s/edit/' % unavailability_calendar.pk |
|
4623 |
app.get(url, status=403) |
|
4624 |
group = manager_user.groups.all()[0] |
|
4625 |
unavailability_calendar.view_role = group |
|
4626 |
unavailability_calendar.edit_role = None |
|
4627 |
unavailability_calendar.save() |
|
4628 |
app.get(url, status=403) |
|
4629 |
unavailability_calendar.view_role = None |
|
4630 |
unavailability_calendar.edit_role = group |
|
4631 |
unavailability_calendar.save() |
|
4632 |
app.get(url) |
|
4633 | ||
4634 | ||
4635 |
def test_unavailability_calendar_delete_permissions(app, manager_user): |
|
4636 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4637 |
app = login(app, username='manager', password='manager') |
|
4638 |
url = '/manage/unavailability-calendar/%s/delete/' % unavailability_calendar.pk |
|
4639 |
app.get(url, status=403) |
|
4640 |
group = manager_user.groups.all()[0] |
|
4641 |
unavailability_calendar.view_role = group |
|
4642 |
unavailability_calendar.edit_role = None |
|
4643 |
unavailability_calendar.save() |
|
4644 |
app.get(url, status=403) |
|
4645 |
unavailability_calendar.view_role = None |
|
4646 |
unavailability_calendar.edit_role = group |
|
4647 |
unavailability_calendar.save() |
|
4648 |
app.get(url, status=403) |
|
4649 | ||
4650 | ||
4651 |
def test_unavailability_calendar_settings_permissions(app, manager_user): |
|
4652 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4653 |
app = login(app, username='manager', password='manager') |
|
4654 |
settings_url = '/manage/unavailability-calendar/%s/settings' % unavailability_calendar.pk |
|
4655 |
app.get(settings_url, status=403) |
|
4656 |
group = manager_user.groups.all()[0] |
|
4657 |
unavailability_calendar.view_role = group |
|
4658 |
unavailability_calendar.edit_role = None |
|
4659 |
unavailability_calendar.save() |
|
4660 |
app.get(settings_url, status=403) |
|
4661 |
unavailability_calendar.view_role = None |
|
4662 |
unavailability_calendar.edit_role = group |
|
4663 |
unavailability_calendar.save() |
|
4664 |
app.get(settings_url) |
|
4665 | ||
4666 | ||
4667 |
def test_unavailability_calendar_add_unavailability_permissions(app, manager_user): |
|
4668 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4669 |
app = login(app, username='manager', password='manager') |
|
4670 |
url = '/manage/unavailability-calendar/%s/add-unavailability' % unavailability_calendar.pk |
|
4671 |
app.get(url, status=403) |
|
4672 |
group = manager_user.groups.all()[0] |
|
4673 |
unavailability_calendar.view_role = group |
|
4674 |
unavailability_calendar.edit_role = None |
|
4675 |
unavailability_calendar.save() |
|
4676 |
app.get(url, status=403) |
|
4677 |
unavailability_calendar.view_role = None |
|
4678 |
unavailability_calendar.edit_role = group |
|
4679 |
unavailability_calendar.save() |
|
4680 |
app.get(url) |
|
4681 | ||
4682 | ||
4683 |
def test_unavailability_calendar_edit_unavailability_permissions(app, manager_user): |
|
4684 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4685 |
time_period_exception = TimePeriodException.objects.create( |
|
4686 |
unavailability_calendar=unavailability_calendar, |
|
4687 |
start_datetime=now() - datetime.timedelta(days=2), |
|
4688 |
end_datetime=now() - datetime.timedelta(days=1), |
|
4689 |
) |
|
4690 |
app = login(app, username='manager', password='manager') |
|
4691 |
url = '/manage/time-period-exceptions/%s/edit' % time_period_exception.pk |
|
4692 |
app.get(url, status=403) |
|
4693 |
group = manager_user.groups.all()[0] |
|
4694 |
unavailability_calendar.view_role = group |
|
4695 |
unavailability_calendar.edit_role = None |
|
4696 |
unavailability_calendar.save() |
|
4697 |
app.get(url, status=403) |
|
4698 |
unavailability_calendar.view_role = None |
|
4699 |
unavailability_calendar.edit_role = group |
|
4700 |
unavailability_calendar.save() |
|
4701 |
app.get(url) |
|
4702 | ||
4703 | ||
4704 |
def test_unavailability_calendar_delete_unavailability_permissions(app, manager_user): |
|
4705 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar 1') |
|
4706 |
time_period_exception = TimePeriodException.objects.create( |
|
4707 |
unavailability_calendar=unavailability_calendar, |
|
4708 |
start_datetime=now() - datetime.timedelta(days=2), |
|
4709 |
end_datetime=now() - datetime.timedelta(days=1), |
|
4710 |
) |
|
4711 |
app = login(app, username='manager', password='manager') |
|
4712 |
url = '/manage/time-period-exceptions/%s/delete' % time_period_exception.pk |
|
4713 |
app.get(url, status=403) |
|
4714 |
group = manager_user.groups.all()[0] |
|
4715 |
unavailability_calendar.view_role = group |
|
4716 |
unavailability_calendar.edit_role = None |
|
4717 |
unavailability_calendar.save() |
|
4718 |
app.get(url, status=403) |
|
4719 |
unavailability_calendar.view_role = None |
|
4720 |
unavailability_calendar.edit_role = group |
|
4721 |
unavailability_calendar.save() |
|
4722 |
app.get(url) |
|
4379 |
- |