manager: differentiate bookings with colors (#39794)
This commit is contained in:
parent
b33972c356
commit
8409186543
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-12-02 17:34
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agendas', '0069_translate_holidays'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BookingColor',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('label', models.CharField(max_length=250, verbose_name='Label')),
|
||||
('index', models.PositiveSmallIntegerField()),
|
||||
(
|
||||
'agenda',
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='booking_colors',
|
||||
to='agendas.Agenda',
|
||||
),
|
||||
),
|
||||
],
|
||||
options={'ordering': ('pk',),},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='booking',
|
||||
name='color',
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='bookings',
|
||||
to='agendas.BookingColor',
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(name='bookingcolor', unique_together=set([('agenda', 'label')]),),
|
||||
]
|
|
@ -1016,6 +1016,27 @@ class Event(models.Model):
|
|||
self.save()
|
||||
|
||||
|
||||
class BookingColor(models.Model):
|
||||
COLOR_COUNT = 8
|
||||
|
||||
agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE, related_name='booking_colors')
|
||||
label = models.CharField(_('Label'), max_length=250)
|
||||
index = models.PositiveSmallIntegerField()
|
||||
|
||||
class Meta:
|
||||
unique_together = ('agenda', 'label')
|
||||
ordering = ('pk',)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.index is None:
|
||||
last_color = BookingColor.objects.filter(agenda=self.agenda).last() or BookingColor(index=-1)
|
||||
self.index = (last_color.index + 1) % self.COLOR_COUNT
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return '%s' % self.label
|
||||
|
||||
|
||||
class Booking(models.Model):
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||
extra_data = JSONField(null=True)
|
||||
|
@ -1040,6 +1061,7 @@ class Booking(models.Model):
|
|||
form_url = models.URLField(blank=True)
|
||||
backoffice_url = models.URLField(blank=True)
|
||||
cancel_callback_url = models.URLField(blank=True)
|
||||
color = models.ForeignKey(BookingColor, null=True, on_delete=models.SET_NULL, related_name='bookings')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
with transaction.atomic():
|
||||
|
|
|
@ -37,7 +37,7 @@ from rest_framework import permissions, serializers, status
|
|||
from rest_framework.views import APIView
|
||||
|
||||
from chrono.api.utils import Response
|
||||
from ..agendas.models import Agenda, Event, Booking, MeetingType, TimePeriodException, Desk
|
||||
from ..agendas.models import Agenda, Event, Booking, MeetingType, TimePeriodException, Desk, BookingColor
|
||||
from ..interval import IntervalSet
|
||||
|
||||
|
||||
|
@ -738,6 +738,7 @@ class SlotSerializer(serializers.Serializer):
|
|||
count = serializers.IntegerField(min_value=1)
|
||||
cancel_booking_id = serializers.CharField(max_length=250, allow_blank=True, allow_null=True)
|
||||
force_waiting_list = serializers.BooleanField(default=False)
|
||||
use_color_for = serializers.CharField(max_length=250, allow_blank=True)
|
||||
|
||||
|
||||
class StringOrListField(serializers.ListField):
|
||||
|
@ -855,6 +856,7 @@ class Fillslots(APIView):
|
|||
extra_data[k] = v
|
||||
|
||||
available_desk = None
|
||||
color = None
|
||||
|
||||
if agenda.accept_meetings():
|
||||
# slots are actually timeslot ids (meeting_type:start_datetime), not events ids.
|
||||
|
@ -913,6 +915,10 @@ class Fillslots(APIView):
|
|||
for slot in all_free_slots:
|
||||
datetimes_by_desk[slot.desk.id].add(slot.start_datetime)
|
||||
|
||||
color_label = payload.get('use_color_for')
|
||||
if color_label:
|
||||
color = BookingColor.objects.get_or_create(agenda=agenda, label=color_label)[0]
|
||||
|
||||
available_desk = None
|
||||
|
||||
if agenda.kind == 'virtual':
|
||||
|
@ -1057,6 +1063,7 @@ class Fillslots(APIView):
|
|||
cancel_callback_url=payload.get('cancel_callback_url', ''),
|
||||
user_display_label=payload.get('user_display_label', ''),
|
||||
extra_data=extra_data,
|
||||
color=color,
|
||||
)
|
||||
if primary_booking is not None:
|
||||
new_booking.primary_booking = primary_booking
|
||||
|
|
|
@ -107,8 +107,16 @@ a.timeperiod-exception-all {
|
|||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
table.agenda-table {
|
||||
border-spacing: 1vw 0;
|
||||
table-layout: fixed;
|
||||
background-color: white;
|
||||
padding: 0.5em 0;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.agenda-table thead th {
|
||||
width: 14vw;
|
||||
padding-bottom: 1ex;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
@ -126,10 +134,8 @@ a.timeperiod-exception-all {
|
|||
box-sizing: border-box;
|
||||
padding: 1.2ex 2ex;
|
||||
vertical-align: top;
|
||||
width: 8ex;
|
||||
font-weight: normal;
|
||||
&.hour {
|
||||
width: 5%;
|
||||
text-align: left;
|
||||
}
|
||||
a {
|
||||
|
@ -137,7 +143,6 @@ a.timeperiod-exception-all {
|
|||
border: 0;
|
||||
}
|
||||
&.weekday {
|
||||
width: 12.5%;
|
||||
padding-top: 3rem;
|
||||
&.today {
|
||||
font-weight: bold;
|
||||
|
@ -149,17 +154,22 @@ a.timeperiod-exception-all {
|
|||
// don't add extra padding on top row
|
||||
padding-top: 1ex;
|
||||
}
|
||||
// hour cells width
|
||||
.agenda-table thead tr:first-child td:first-child,
|
||||
.agenda-table tbody tr:first-child th:not(.weekday) {
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
.agenda-table tbody tr.odd th.hour,
|
||||
.agenda-table tbody tr.odd td {
|
||||
background: #f0f0f0;
|
||||
background: hsla(0, 0%, 0%, 0.04);
|
||||
@media print {
|
||||
border-top: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.agenda-table tbody tr.odd td.other-month {
|
||||
background: #f8f8f8;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
|
@ -170,15 +180,6 @@ a.timeperiod-exception-all {
|
|||
border: 0;
|
||||
}
|
||||
|
||||
.agenda-table.month-view {
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.agenda-table.month-view tbody td {
|
||||
border: 5px solid white;
|
||||
border-width: 0 5px;
|
||||
}
|
||||
|
||||
@for $i from 1 through 60 {
|
||||
table.hourspan-#{$i} tbody td {
|
||||
height: calc(#{$i} * 2.5em);
|
||||
|
@ -187,42 +188,86 @@ a.timeperiod-exception-all {
|
|||
|
||||
.agenda-table tbody td div {
|
||||
box-sizing: border-box;
|
||||
padding: 1ex;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
&.opening-hours, &.exception-hours {
|
||||
z-index: 1;
|
||||
background: #b1ea4d linear-gradient(135deg, #b1ea4d 0%, #459522 100%);
|
||||
opacity: 0.6;
|
||||
background:
|
||||
linear-gradient(
|
||||
135deg,
|
||||
hsla(65, 65%, 94%, 0.4) 20%,
|
||||
hsla(65, 55%, 92%, 0.7) 70%,
|
||||
hsl(65, 50%, 90%) 90%) fixed;
|
||||
left: 0.5ex;
|
||||
width: calc(100% - 1ex);
|
||||
}
|
||||
&.opening-hours {
|
||||
border-left: 0.5em solid hsl(57, 65%, 85%);
|
||||
}
|
||||
&.exception-hours {
|
||||
background: #fee linear-gradient(135deg, #fee 0%, #fdd 100%);
|
||||
background:
|
||||
repeating-linear-gradient(
|
||||
135deg,
|
||||
hsla(10, 10%, 75%, 0.7) 0,
|
||||
hsla(10, 10%, 80%, 0.55) 10px,
|
||||
transparent 11px,
|
||||
transparent 20px);
|
||||
text-align: center;
|
||||
}
|
||||
&.booking {
|
||||
background: #eef linear-gradient(135deg, #eef 0%, #ddf 100%);
|
||||
box-shadow: 0 0 1px 0 #2d2dad;
|
||||
width: calc(100% - 2ex);
|
||||
border: 1px solid #aaa;
|
||||
left: 0.5ex;
|
||||
color: hsl(210, 84%, 40%);
|
||||
padding: 1ex;
|
||||
background:
|
||||
linear-gradient(
|
||||
110deg,
|
||||
hsla(0, 0%, 100%, 0.85) 0%,
|
||||
hsla(0, 0%, 100%, 0.65) 100%)
|
||||
currentColor;
|
||||
width: calc(100% - 1ex);
|
||||
border-left: .5em solid;
|
||||
border-bottom: 1px solid;
|
||||
border-color: currentColor;
|
||||
|
||||
z-index: 2;
|
||||
&:hover {
|
||||
z-index: 3;
|
||||
height: auto !important;
|
||||
}
|
||||
> * {
|
||||
color: hsla(0, 0%, 0%, 0.6);
|
||||
}
|
||||
a {
|
||||
// color: currentColor;
|
||||
color: hsla(0, 0%, 0%, 0.7);
|
||||
border-bottom-color: inherit;
|
||||
|
||||
&:hover {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.monthview tbody td div.booking {
|
||||
padding: 0;
|
||||
transition: width 100ms ease-in, left 100ms ease-in, color 200ms ease-in;
|
||||
box-shadow: 0 0 0 0 #888;
|
||||
transition:
|
||||
width 100ms ease-in,
|
||||
left 100ms ease-in,
|
||||
color 200ms ease-in,
|
||||
box-shadow 200ms ease-in,
|
||||
padding 100ms ease-in;
|
||||
text-indent: -9999px;
|
||||
&:not(:hover) {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
&:hover {
|
||||
text-indent: 0;
|
||||
color: inherit;
|
||||
left: 0% !important;
|
||||
width: 100% !important
|
||||
width: 100% !important;
|
||||
box-shadow: 0 0 1em 0 #888;
|
||||
}
|
||||
span.desk {
|
||||
display: block;
|
||||
|
@ -311,3 +356,33 @@ div.booking a.cancel {
|
|||
p.email-subject {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// booking colors
|
||||
$booking-colors: (
|
||||
0: hsl(30, 100%, 46%),
|
||||
1: hsl(120, 57%, 35%),
|
||||
2: hsl(270, 40%, 50%),
|
||||
3: hsl(355, 80%, 45%),
|
||||
4: hsl(10, 70%, 30%),
|
||||
5: hsl(60, 98%, 30%),
|
||||
6: hsl(150, 57%, 25%),
|
||||
7: hsl(320, 70%, 60%)
|
||||
);
|
||||
.booking-colors {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.booking-color-label {
|
||||
padding: .33em .66em;
|
||||
border-radius: 0.33em;
|
||||
color: hsla(0, 0%, 100%, 0.9) !important;
|
||||
font-weight: bold;
|
||||
font-size: .65em;
|
||||
}
|
||||
@each $index, $color in $booking-colors {
|
||||
.agenda-table tbody td div.booking-color-#{$index} {
|
||||
color: $color;
|
||||
}
|
||||
.booking-bg-color-#{$index} {
|
||||
background-color: $color;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{% load i18n %}
|
||||
|
||||
<div class="booking-colors">
|
||||
<strong>{% trans "Booking colors:" %}</strong>
|
||||
{% for color in colors %}
|
||||
<span class="booking-color-label booking-bg-color-{{ color.index }}">{{ color }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -68,11 +68,12 @@
|
|||
{% endif %}
|
||||
|
||||
{% for booking in desk_info.bookings %}
|
||||
<div class="booking"
|
||||
<div class="booking{% if booking.color %} booking-color-{{ booking.color.index }}{% endif %}"
|
||||
style="height: {{ booking.css_height }}%; min-height: {{ booking.css_height }}%; top: {{ booking.css_top }}%;"
|
||||
><span class="start-time">{{booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
|
||||
<a {% if booking.backoffice_url %}href="{{booking.backoffice_url}}"{% endif %}>{{ booking.meetings_display }}</a>
|
||||
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=booking.event.agenda_id booking_pk=booking.pk %}?next={{ request.path }}">{% trans "Cancel" %}</a>
|
||||
{% if booking.color %}<span class="booking-color-label booking-bg-color-{{ booking.color.index }}">{{ booking.color }}</span>{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</td>
|
||||
|
@ -90,4 +91,8 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% if booking_colors %}
|
||||
{% include "chrono/booking_color_legend.html" with colors=booking_colors %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -34,11 +34,12 @@
|
|||
{% endfor %}
|
||||
|
||||
{% for slot in day.infos.booked_slots %}
|
||||
<div class="booking" style="left:{{ slot.css_left|stringformat:".1f" }}%;height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%">
|
||||
<div class="booking{% if slot.booking.color %} booking-color-{{ slot.booking.color.index }}{% endif %}" style="left:{{ slot.css_left|stringformat:".1f" }}%;height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;">
|
||||
<span class="start-time">{{slot.booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
|
||||
<a {% if slot.booking.backoffice_url %}href="{{slot.booking.backoffice_url}}"{% endif %}>{{ slot.booking.meetings_display }}</a>
|
||||
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=slot.booking.event.agenda_id booking_pk=slot.booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a>
|
||||
{% if not single_desk %}<span class="desk">{{ slot.desk }}</span>{% endif %}
|
||||
{% if slot.booking.color %}<span class="booking-color-label booking-bg-color-{{ slot.booking.color.index }}">{{ slot.booking.color }}</span>{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
@ -58,4 +59,8 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% if booking_colors %}
|
||||
{% include "chrono/booking_color_legend.html" with colors=booking_colors %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -70,6 +70,7 @@ from chrono.agendas.models import (
|
|||
AgendaNotificationsSettings,
|
||||
AgendaReminderSettings,
|
||||
UnavailabilityCalendar,
|
||||
BookingColor,
|
||||
)
|
||||
|
||||
from .forms import (
|
||||
|
@ -883,6 +884,9 @@ class AgendaDateView(DateMixin, ViewableAgendaMixin):
|
|||
context['hour_span'] = max(60 // self.agenda.get_base_meeting_duration(), 1)
|
||||
except ValueError: # no meeting types defined
|
||||
context['hour_span'] = 1
|
||||
context['booking_colors'] = BookingColor.objects.filter(
|
||||
agenda=self.agenda, bookings__event__in=self.object_list
|
||||
).distinct()
|
||||
context['user_can_manage'] = self.agenda.can_be_managed(self.request.user)
|
||||
return context
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ from chrono.agendas.models import (
|
|||
TimePeriodException,
|
||||
UnavailabilityCalendar,
|
||||
VirtualMember,
|
||||
BookingColor,
|
||||
)
|
||||
import chrono.api.views
|
||||
|
||||
|
@ -1265,6 +1266,50 @@ def test_booking_api_meeting(app, meetings_agenda, user):
|
|||
assert Booking.objects.count() == 2
|
||||
|
||||
|
||||
def test_booking_api_meeting_colors(app, user):
|
||||
agenda = Agenda.objects.create(
|
||||
label='foo', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=56
|
||||
)
|
||||
meeting_type = MeetingType.objects.create(agenda=agenda, label='Blah', duration=30)
|
||||
default_desk = Desk.objects.create(agenda=agenda, label='Desk 1')
|
||||
time_period = TimePeriod.objects.create(
|
||||
weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0), desk=default_desk
|
||||
)
|
||||
datetimes_resp = app.get('/api/agenda/meetings/%s/datetimes/' % meeting_type.id)
|
||||
event_id = datetimes_resp.json['data'][2]['id']
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
||||
resp = app.post(
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id), params={'use_color_for': 'Cooking',},
|
||||
)
|
||||
booking = Booking.objects.get(id=resp.json['booking_id'])
|
||||
assert booking.color.label == 'Cooking'
|
||||
assert booking.color.index == 0
|
||||
|
||||
event_id = datetimes_resp.json['data'][3]['id']
|
||||
resp = app.post_json(
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id), params={'use_color_for': 'Cooking',},
|
||||
)
|
||||
new_booking = Booking.objects.get(id=resp.json['booking_id'])
|
||||
assert new_booking.color.index == 0
|
||||
|
||||
event_id = datetimes_resp.json['data'][4]['id']
|
||||
resp = app.post_json(
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id), params={'use_color_for': 'Swimming',},
|
||||
)
|
||||
new_booking = Booking.objects.get(id=resp.json['booking_id'])
|
||||
assert new_booking.color.label == 'Swimming'
|
||||
assert new_booking.color.index == 1
|
||||
|
||||
for i in range((BookingColor.COLOR_COUNT * 2) - 2):
|
||||
event_id = datetimes_resp.json['data'][i]['id']
|
||||
resp = app.post_json(
|
||||
'/api/agenda/%s/fillslot/%s/' % (agenda.pk, event_id), params={'use_color_for': str(i),},
|
||||
)
|
||||
assert BookingColor.objects.count() == BookingColor.COLOR_COUNT * 2
|
||||
assert BookingColor.objects.distinct('index').order_by().count() == BookingColor.COLOR_COUNT
|
||||
|
||||
|
||||
def test_booking_api_meeting_with_resources(app, user):
|
||||
tomorrow = datetime.date.today() + datetime.timedelta(days=1)
|
||||
tomorrow_str = tomorrow.isoformat()
|
||||
|
|
|
@ -2996,7 +2996,7 @@ def test_agenda_day_view(app, admin_user, manager_user, api_user):
|
|||
resp = app.get(
|
||||
'/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200
|
||||
)
|
||||
assert len(ctx.captured_queries) == 12
|
||||
assert len(ctx.captured_queries) == 13
|
||||
# day is displaying rows from 10am to 6pm,
|
||||
# opening hours, 10am to 1pm gives top: 300%
|
||||
# rest of the day, 1pm to 6(+1)pm gives 600%
|
||||
|
@ -3305,7 +3305,7 @@ def test_agenda_month_view(app, admin_user, manager_user, api_user):
|
|||
)
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
|
||||
assert len(ctx.captured_queries) == 9
|
||||
assert len(ctx.captured_queries) == 10
|
||||
assert resp.pyquery.find('.exception-hours')[0].attrib == {
|
||||
'class': 'exception-hours',
|
||||
'style': 'height:800.0%;top:0.0%;width:48.0%;left:50.0%;',
|
||||
|
@ -3957,7 +3957,7 @@ def test_virtual_agenda_day_view(app, admin_user, manager_user):
|
|||
resp = app.get(
|
||||
'/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day), status=200
|
||||
)
|
||||
assert len(ctx.captured_queries) == 13
|
||||
assert len(ctx.captured_queries) == 14
|
||||
# day is displaying rows from 10am to 6pm,
|
||||
# opening hours, 10am to 1pm gives top: 300%
|
||||
# rest of the day, 1pm to 6(+1)pm gives 600%
|
||||
|
@ -4055,7 +4055,7 @@ def test_virtual_agenda_month_view(app, admin_user):
|
|||
)
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, today.month))
|
||||
assert len(ctx.captured_queries) == 10
|
||||
assert len(ctx.captured_queries) == 11
|
||||
assert resp.pyquery.find('.exception-hours')[0].attrib == {
|
||||
'class': 'exception-hours',
|
||||
'style': 'height:800.0%;top:0.0%;width:48.0%;left:1.0%;',
|
||||
|
@ -5281,3 +5281,72 @@ def test_manager_agenda_booking_delays(app, admin_user):
|
|||
assert '42 days' in resp.text
|
||||
agenda.refresh_from_db()
|
||||
assert agenda.maximal_booking_delay == 42
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'view',
|
||||
(
|
||||
'/manage/agendas/%(agenda)s/%(year)d/%(month)d/%(day)d/',
|
||||
'/manage/agendas/%(agenda)s/%(year)d/%(month)d/',
|
||||
),
|
||||
)
|
||||
def test_agenda_booking_colors(app, admin_user, api_user, view):
|
||||
agenda = Agenda.objects.create(label='New Example', kind='meetings')
|
||||
desk = Desk.objects.create(agenda=agenda, label='New Desk')
|
||||
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
|
||||
today = datetime.date.today()
|
||||
timeperiod = TimePeriod.objects.create(
|
||||
desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
|
||||
)
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
datetimes_resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
|
||||
booking_url = datetimes_resp.json['data'][0]['api']['fillslot_url']
|
||||
|
||||
# book first slot without colors
|
||||
resp = app.post(booking_url)
|
||||
date = Booking.objects.all()[0].event.start_datetime
|
||||
|
||||
app.reset()
|
||||
login(app)
|
||||
|
||||
url = view % {'agenda': agenda.id, 'year': date.year, 'month': date.month, 'day': date.day}
|
||||
resp = app.get(url)
|
||||
assert len(resp.pyquery.find('div.booking')) == 1
|
||||
assert 'booking-color-' not in resp.text
|
||||
assert 'Booking colors:' not in resp.text
|
||||
|
||||
app.reset()
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
booking_url2 = datetimes_resp.json['data'][1]['api']['fillslot_url']
|
||||
booking_url3 = datetimes_resp.json['data'][2]['api']['fillslot_url']
|
||||
resp = app.post_json(booking_url2, params={'use_color_for': 'Cooking'})
|
||||
resp = app.post_json(booking_url3, params={'use_color_for': 'Cooking'})
|
||||
booking = Booking.objects.get(pk=resp.json['booking_id'])
|
||||
|
||||
app.reset()
|
||||
login(app)
|
||||
|
||||
resp = app.get(url)
|
||||
assert len(resp.pyquery.find('div.booking')) == 3
|
||||
assert len(resp.pyquery.find('div.booking.booking-color-%s' % booking.color.index)) == 2
|
||||
assert 'Booking colors:' in resp.text
|
||||
assert len(resp.pyquery.find('div.booking-colors span.booking-color-label')) == 1
|
||||
assert resp.text.count('Cooking') == 3 # 2 bookings + legend
|
||||
|
||||
app.reset()
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
booking_url4 = datetimes_resp.json['data'][3]['api']['fillslot_url']
|
||||
resp = app.post_json(booking_url4, params={'use_color_for': 'Swimming'})
|
||||
new_booking = Booking.objects.get(pk=resp.json['booking_id'])
|
||||
|
||||
app.reset()
|
||||
login(app)
|
||||
|
||||
resp = app.get(url)
|
||||
assert len(resp.pyquery.find('div.booking')) == 4
|
||||
assert len(resp.pyquery.find('div.booking.booking-color-%s' % booking.color.index)) == 2
|
||||
assert len(resp.pyquery.find('div.booking.booking-color-%s' % new_booking.color.index)) == 1
|
||||
assert resp.text.count('Swimming') == 2 # 1 booking + legend
|
||||
assert 'Booking colors:' in resp.text
|
||||
assert len(resp.pyquery.find('div.booking-colors span.booking-color-label')) == 2
|
||||
|
|
Loading…
Reference in New Issue