manager: weekly wiew for agendas & resources (#33404)

This commit is contained in:
Lauréline Guérin 2022-10-25 18:47:48 +02:00
parent a9236fac0d
commit 863dc6ce70
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
20 changed files with 1439 additions and 189 deletions

View File

@ -12,7 +12,12 @@ class Migration(migrations.Migration):
model_name='agenda',
name='default_view',
field=models.CharField(
choices=[('day', 'Day view'), ('month', 'Month view'), ('open_events', 'Open events')],
choices=[
('day', 'Day view'),
('week', 'Week view'),
('month', 'Month view'),
('open_events', 'Open events'),
],
max_length=20,
verbose_name='Default view',
),

View File

@ -70,6 +70,7 @@ AGENDA_KINDS = (
AGENDA_VIEWS = (
('day', _('Day view')),
('week', _('Week view')),
('month', _('Month view')),
('open_events', _('Open events')),
)

View File

@ -10,8 +10,10 @@ $(function() {
$('.date-picker button').on('click', function() {
if ($('[name=day]').val()) {
window.location = '../../../' + $('[name=year]').val() + '/' + $('[name=month]').val() + '/' + $('[name=day]').val() + '/';
} else {
} else if ($('[name=month]').val()) {
window.location = '../../' + $('[name=year]').val() + '/' + $('[name=month]').val() + '/';
} else {
window.location = '../../../' + $('[name=year]').val() + '/week/' + $('[name=week]').val() + '/';
}
return false;
});

View File

@ -0,0 +1,32 @@
{% extends "chrono/manager_agenda_view.html" %}
{% load i18n %}
{% block bodyargs %}class="weekview"{% endblock %}
{% block breadcrumb %}
{{ block.super }}
<a>{{ view.date|date:"F Y" }}</a>
{% endblock %}
{% block appbar %}
<h2>
<a href="{{ view.get_previous_week_url }}"></a>
<span class="date-title">{{ view.date|date:_("Y \w\e\e\k W") }}</span>
{% with selected_week=view.date|date:"W" selected_year=view.date|date:"Y" %}
<div class="date-picker" style="display: none">
<select name="week">{% for week, week_label in view.get_weeks %}<option value="{{ week }}" {% if selected_week == week %}selected{% endif %}>{{ week_label }}</option>{% endfor %}</select>
<select name="year">{% for year in view.get_years %}<option value="{{ year }}" {% if selected_year == year %}selected{% endif %}>{{ year }}</option>{% endfor %}</select>
<button>{% trans 'Set Date' %}</button>
</div>
{% endwith %}
<a href="{{ view.get_next_week_url }}"></a>
</h2>
<span class="actions">
{% block actions %}
{% if user_can_manage %}
<a href="{{ agenda.get_settings_url }}">{% trans 'Settings' %}</a>
{% endif %}
<a href="" onclick="window.print()">{% trans 'Print' %}</a>
{% endblock %}
</span>
{% endblock %}

View File

@ -11,6 +11,7 @@
{{ block.super }}
<a href="{% url 'chrono-manager-agenda-open-events-view' pk=agenda.pk %}">{% trans 'Open events' %}</a>
<a href="{% url 'chrono-manager-agenda-month-view' pk=agenda.pk year=view.date|date:"Y" month=view.date|date:"n" %}">{% trans 'Month view' %}</a>
<a href="{% url 'chrono-manager-agenda-week-view' pk=agenda.pk year=view.date|date:"Y" week=view.date|date:"W" %}">{% trans 'Week view' %}</a>
{% endblock %}
{% block content %}

View File

@ -11,6 +11,7 @@
</ul>
{{ block.super }}
<a href="{% url 'chrono-manager-agenda-open-events-view' pk=agenda.pk %}">{% trans 'Open events' %}</a>
<a href="{% url 'chrono-manager-agenda-week-view' pk=agenda.pk year=view.date|date:"Y" week=view.date|date:"W" %}">{% trans 'Week view' %}</a>
<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.pk year=view.date|date:"Y" month=view.date|date:"m" day=view.date|date:"d" %}">{% trans 'Day view' %}</a>
{% endblock %}

View File

@ -0,0 +1,44 @@
{% extends "chrono/manager_agenda_week_view.html" %}
{% load i18n %}
{% block actions %}
<a class="extra-actions-menu-opener"></a>
<ul class="extra-actions-menu">
<li><a href="{% url 'chrono-manager-event-cancellation-report-list' pk=agenda.pk %}">{% trans 'Cancellation error reports' %}</a></li>
{% if agenda.subscriptions.exists %}
<li><a href="{% url 'chrono-manager-events-timesheet' pk=agenda.pk %}">{% trans 'Timesheet' %}</a></li>
{% endif %}
</ul>
{{ block.super }}
<a href="{% url 'chrono-manager-agenda-open-events-view' pk=agenda.pk %}">{% trans 'Open events' %}</a>
<a href="{% url 'chrono-manager-agenda-month-view' pk=agenda.pk year=view.date|date:"Y" month=view.date|date:"m" %}">{% trans 'Month view' %}</a>
<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.pk year=view.date|date:"Y" month=view.date|date:"m" day=view.date|date:"d" %}">{% trans 'Day view' %}</a>
{% endblock %}
{% block content %}
<div class="section">
<h3>{% trans "Events" %}</h3>
{% include 'chrono/manager_event_cancellation_report_notice.html' %}
<div>
{% if object_list %}
<ul class="objects-list single-links">
{% for object in object_list %}
{% if object.is_exception %}
<li><a class="disabled">{% trans "Exception:"%} {{ object }}</a></li>
{% else %}
{% include 'chrono/manager_agenda_event_fragment.html' with event=object %}
{% endif %}
{% endfor %}
</ul>
{% include "gadjo/pagination.html" %}
{% else %}
<div class="big-msg-info">
{% blocktrans trimmed %}
This week doesn't have any event configured.
{% endblocktrans %}
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -4,6 +4,7 @@
{% block actions %}
{{ block.super }}
<a href="{% url 'chrono-manager-agenda-month-view' pk=agenda.id year=view.date|date:"Y" month=view.date|date:"n" %}">{% trans 'Month view' %}</a>
<a href="{% url 'chrono-manager-agenda-week-view' pk=agenda.id year=view.date|date:"Y" week=view.date|date:"W" %}">{% trans 'Week view' %}</a>
{% endblock %}
{% block content %}

View File

@ -3,64 +3,10 @@
{% block actions %}
{{ block.super }}
<a href="{% url 'chrono-manager-agenda-week-view' pk=agenda.id year=view.date|date:"Y" week=view.date|date:"W" %}">{% trans 'Week view' %}</a>
<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.id year=view.date|date:"Y" month=view.date|date:"m" day=view.date|date:"d" %}">{% trans 'Day view' %}</a>
{% endblock %}
{% block content %}
{% for week_days in view.get_timetable_infos %}
{% if forloop.first %}
<table class="agenda-table month-view {% if single_desk %}single-desk{% endif %}">
<tbody>
{% endif %}
<tr>
<th></th>
{% for day in week_days.days %}
<th class="weekday {% if day.today %}today{% endif %}">{% if not day.other_month %}<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.id year=day.date|date:"Y" month=day.date|date:"m" day=day.date|date:"d" %}">{{ day.date|date:"l j" }}</a>{% endif %}</th>
{% endfor %}
</tr>
{% for hour in week_days.periods %}
<tr class="{% cycle 'odd' 'even' %}">
<th class="hour">{{ hour|date:"TIME_FORMAT" }}</th>
{% for day in week_days.days %}
<td class="{% if day.other_month %}other-month{% endif %} {% if day.today %}today{% endif %}">
{% if forloop.parentloop.first %}
{% for slot in day.infos.opening_hours %}
<div class="opening-hours" style="height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;left:{{ slot.css_left|stringformat:".1f" }}%;"></div>
{% endfor %}
{% for slot in day.infos.exceptions %}
<div class="exception-hours" style="height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;left:{{ slot.css_left|stringformat:".1f" }}%;" {% if slot.label %}title="{{ slot.label }}"{% endif %}></div>
{% endfor %}
{% for slot in day.infos.booked_slots %}
<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.get_backoffice_url %}href="{{slot.booking.get_backoffice_url}}"{% endif %}>{{ slot.booking.get_user_block }}</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 %}
</td>
{% endfor %}
</tr>
{% endfor %}
{% resetcycle %}
{% if forloop.last %}
</tbody>
</table>
{% endif %}
{% empty %}
<div class="closed-for-the-day">
<p>{% trans "No opening hours this month." %}</p>
</div>
{% endfor %}
{% if booking_colors %}
{% include "chrono/booking_color_legend.html" with colors=booking_colors %}
{% endif %}
{% include "chrono/manager_meetings_agenda_week_timetable_fragment.html" %}
{% endblock %}

View File

@ -0,0 +1,59 @@
{% load i18n %}
{% for week_days in view.get_timetable_infos %}
{% if forloop.first %}
<table class="agenda-table {{ kind }}-view {% if kind == 'week' %}hourspan-{{ hour_span }}{% endif %} {% if single_desk %}single-desk{% endif %}">
<tbody>
{% endif %}
<tr>
<th></th>
{% for day in week_days.days %}
<th class="weekday {% if day.today %}today{% endif %}">{% if kind == 'month' and not day.other_month or kind == 'week' %}<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.id year=day.date|date:"Y" month=day.date|date:"m" day=day.date|date:"d" %}">{{ day.date|date:"l j" }}{% if kind == 'week' %}<br />{{ day.date|date:"F" }}{% endif %}</a>{% endif %}</th>
{% endfor %}
</tr>
{% for hour in week_days.periods %}
<tr class="{% cycle 'odd' 'even' %}">
<th class="hour">{{ hour|date:"TIME_FORMAT" }}</th>
{% for day in week_days.days %}
<td class="{% if kind == 'month' and day.other_month %}other-month{% endif %} {% if day.today %}today{% endif %}">
{% if forloop.parentloop.first %}
{% for slot in day.infos.opening_hours %}
<div class="opening-hours" style="height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;left:{{ slot.css_left|stringformat:".1f" }}%;"></div>
{% endfor %}
{% for slot in day.infos.exceptions %}
<div class="exception-hours" style="height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;left:{{ slot.css_left|stringformat:".1f" }}%;" {% if slot.label %}title="{{ slot.label }}"{% endif %}></div>
{% endfor %}
{% for slot in day.infos.booked_slots %}
<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.get_backoffice_url %}href="{{slot.booking.get_backoffice_url}}"{% endif %}>{{ slot.booking.get_user_block }}</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 %}
</td>
{% endfor %}
</tr>
{% endfor %}
{% resetcycle %}
{% if forloop.last %}
</tbody>
</table>
{% endif %}
{% empty %}
<div class="closed-for-the-day">
{% if kind == 'month' %}
<p>{% trans "No opening hours this month." %}</p>
{% else %}
<p>{% trans "No opening hours this week." %}</p>
{% endif %}
</div>
{% endfor %}
{% if booking_colors %}
{% include "chrono/booking_color_legend.html" with colors=booking_colors %}
{% endif %}

View File

@ -0,0 +1,12 @@
{% extends "chrono/manager_agenda_week_view.html" %}
{% load i18n %}
{% block actions %}
{{ block.super }}
<a href="{% url 'chrono-manager-agenda-month-view' pk=agenda.id year=view.date|date:"Y" month=view.date|date:"m" %}">{% trans 'Month view' %}</a>
<a href="{% url 'chrono-manager-agenda-day-view' pk=agenda.id year=view.date|date:"Y" month=view.date|date:"m" day=view.date|date:"d" %}">{% trans 'Day view' %}</a>
{% endblock %}
{% block content %}
{% include "chrono/manager_meetings_agenda_week_timetable_fragment.html" %}
{% endblock %}

View File

@ -23,6 +23,7 @@
{% endblock %}
{% block appbar-extras %}
<a href="{% url 'chrono-manager-resource-month-view' pk=resource.pk year=view.date|date:"Y" month=view.date|date:"n" %}">{% trans 'Month view' %}</a>
<a href="{% url 'chrono-manager-resource-week-view' pk=resource.pk year=view.date|date:"Y" week=view.date|date:"W" %}">{% trans 'Week view' %}</a>
{% endblock %}
{% block content %}

View File

@ -22,8 +22,10 @@
{% endif %}
{% now "Y" as today_year %}
{% now "n" as today_month %}
{% now "W" as today_week %}
{% now "j" as today_day %}
<a href="{% url 'chrono-manager-resource-month-view' pk=resource.pk year=today_year month=today_month %}">{% trans 'Month view' %}</a>
<a href="{% url 'chrono-manager-resource-week-view' pk=resource.pk year=today_year week=today_week %}">{% trans 'Week view' %}</a>
<a href="{% url 'chrono-manager-resource-day-view' pk=resource.pk year=today_year month=today_month day=today_day %}">{% trans 'Day view' %}</a>
{% endblock %}
</span>

View File

@ -23,48 +23,10 @@
</h2>
{% endblock %}
{% block appbar-extras %}
<a href="{% url 'chrono-manager-resource-day-view' pk=resource.pk year=view.date|date:"Y" month=view.date|date:"n" day=1 %}">{% trans 'Day view' %}</a>
<a href="{% url 'chrono-manager-resource-week-view' pk=resource.pk year=view.date|date:"Y" week=view.date|date:"W" %}">{% trans 'Week view' %}</a>
<a href="{% url 'chrono-manager-resource-day-view' pk=resource.pk year=view.date|date:"Y" month=view.date|date:"n" day=view.date|date:"d" %}">{% trans 'Day view' %}</a>
{% endblock %}
{% block content %}
{% for week_days in view.get_timetable_infos %}
{% if forloop.first %}
<table class="agenda-table month-view single-desk">
<tbody>
{% endif %}
<tr>
<th></th>
{% for day in week_days.days %}
<th class="weekday {% if day.today %}today{% endif %}">{% if not day.other_month %}<a href="{% url 'chrono-manager-resource-day-view' pk=resource.pk year=day.date|date:"Y" month=day.date|date:"m" day=day.date|date:"j" %}">{{ day.date|date:"l j" }}</a>{% endif %}</th>
{% endfor %}
</tr>
{% for hour in week_days.periods %}
<tr class="{% cycle 'odd' 'even' %}">
<th class="hour">{{ hour|date:"TIME_FORMAT" }}</th>
{% for day in week_days.days %}
<td class="{% if day.other_month %}other-month{% endif %} {% if day.today %}today{% endif %}">
{% if forloop.parentloop.first %}
{% for slot in day.infos.booked_slots %}
<div class="booking" style="height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%">
<span class="start-time">{{ slot.booking.event.start_datetime|date:"TIME_FORMAT" }}</span>
<a {% if slot.booking.get_backoffice_url %}href="{{ slot.booking.get_backoffice_url }}"{% endif %}>{{ slot.booking.get_user_block }}</a>
</div>
{% endfor %}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
{% if forloop.last %}
</tbody>
</table>
{% endif %}
{% empty %}
<div class="closed-for-the-day">
<p>{% trans "No bookings this month." %}</p>
</div>
{% endfor %}
{% include "chrono/manager_resource_week_timetable_fragment.html" %}
{% endblock %}

View File

@ -0,0 +1,43 @@
{% load i18n %}
{% for week_days in view.get_timetable_infos %}
{% if forloop.first %}
<table class="agenda-table {{ kind }}-view {% if kind == 'week' %}hourspan-{{ hour_span }}{% endif %} single-desk">
<tbody>
{% endif %}
<tr>
<th></th>
{% for day in week_days.days %}
<th class="weekday {% if day.today %}today{% endif %}">{% if kind == 'month' and not day.other_month or kind == 'week' %}<a href="{% url 'chrono-manager-resource-day-view' pk=resource.pk year=day.date|date:"Y" month=day.date|date:"m" day=day.date|date:"d" %}">{{ day.date|date:"l j" }}{% if kind == 'week' %}<br />{{ day.date|date:"F" }}{% endif %}</a>{% endif %}</th>
{% endfor %}
</tr>
{% for hour in week_days.periods %}
<tr class="{% cycle 'odd' 'even' %}">
<th class="hour">{{ hour|date:"TIME_FORMAT" }}</th>
{% for day in week_days.days %}
<td class="{% if kind == 'month' and day.other_month %}other-month{% endif %} {% if day.today %}today{% endif %}">
{% if forloop.parentloop.first %}
{% for slot in day.infos.booked_slots %}
<div class="booking" style="height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%">
<span class="start-time">{{ slot.booking.event.start_datetime|date:"TIME_FORMAT" }}</span>
<a {% if slot.booking.get_backoffice_url %}href="{{ slot.booking.get_backoffice_url }}"{% endif %}>{{ slot.booking.get_user_block }}</a>
</div>
{% endfor %}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
{% if forloop.last %}
</tbody>
</table>
{% endif %}
{% empty %}
<div class="closed-for-the-day">
{% if kind == 'month' %}
<p>{% trans "No bookings this month." %}</p>
{% else %}
<p>{% trans "No bookings this week." %}</p>
{% endif %}
</div>
{% endfor %}

View File

@ -0,0 +1,32 @@
{% extends "chrono/manager_resource_detail.html" %}
{% load i18n %}
{% block bodyargs %}class="weekview"{% endblock %}
{% block breadcrumb %}
{{ block.super }}
<a>{{ view.date|date:"F Y" }}</a>
{% endblock %}
{% block appbar-title %}
<h2>
<a href="{{ view.get_previous_week_url }}"></a>
<span class="date-title">{{ view.date|date:_("Y \w\e\e\k W") }}</span>
{% with selected_week=view.date|date:"W" selected_year=view.date|date:"Y" %}
<div class="date-picker" style="display: none">
<select name="week">{% for week, week_label in view.get_weeks %}<option value="{{ week }}" {% if selected_week == week %}selected{% endif %}>{{ week_label }}</option>{% endfor %}</select>
<select name="year">{% for year in view.get_years %}<option value="{{ year }}" {% if selected_year == year %}selected{% endif %}>{{ year }}</option>{% endfor %}</select>
<button>{% trans 'Set Date' %}</button>
</div>
{% endwith %}
<a href="{{ view.get_next_week_url }}"></a>
</h2>
{% endblock %}
{% block appbar-extras %}
<a href="{% url 'chrono-manager-resource-month-view' pk=resource.pk year=view.date|date:"Y" month=view.date|date:"n" %}">{% trans 'Month view' %}</a>
<a href="{% url 'chrono-manager-resource-day-view' pk=resource.pk year=view.date|date:"Y" month=view.date|date:"n" day=view.date|date:"d" %}">{% trans 'Day view' %}</a>
{% endblock %}
{% block content %}
{% include "chrono/manager_resource_week_timetable_fragment.html" %}
{% endblock %}

View File

@ -73,6 +73,11 @@ urlpatterns = [
views.resource_monthly_view,
name='chrono-manager-resource-month-view',
),
re_path(
r'^resource/(?P<pk>\d+)/(?P<year>[0-9]{4})/week/(?P<week>[0-9]+)/$',
views.resource_weekly_view,
name='chrono-manager-resource-week-view',
),
re_path(
r'^resource/(?P<pk>\d+)/(?P<year>[0-9]{4})/(?P<month>[0-9]+)/(?P<day>[0-9]+)/$',
views.resource_day_view,
@ -107,6 +112,16 @@ urlpatterns = [
views.agenda_monthly_view,
name='chrono-manager-agenda-month-view',
),
path(
'agendas/<int:pk>/week/',
views.agenda_week_redirect_view,
name='chrono-manager-agenda-week-redirect-view',
),
re_path(
r'^agendas/(?P<pk>\d+)/(?P<year>[0-9]{4})/week/(?P<week>[0-9]+)/$',
views.agenda_weekly_view,
name='chrono-manager-agenda-week-view',
),
path(
'agendas/<int:pk>/day/',
views.agenda_day_redirect_view,

View File

@ -57,6 +57,7 @@ from django.views.generic import (
TemplateView,
UpdateView,
View,
WeekArchiveView,
)
from django.views.generic.dates import MonthMixin, YearMixin
from weasyprint import HTML
@ -250,6 +251,9 @@ class DateMixin:
def get_months(self):
return [(str(x), MONTHS[x]) for x in range(1, 13)]
def get_weeks(self):
return [(str(x), _('Week %s') % x) for x in range(1, 53)]
def get_years(self):
year = now().year
return [str(x) for x in range(year - 1, year + 5)]
@ -384,28 +388,7 @@ class ResourceDayView(DateMixin, DayArchiveView):
resource_day_view = ResourceDayView.as_view()
class ResourceMonthView(DateMixin, MonthArchiveView):
template_name = 'chrono/manager_resource_month_view.html'
model = Event
month_format = '%m'
date_field = 'start_datetime'
allow_empty = True
allow_future = True
def dispatch(self, request, *args, **kwargs):
self.resource = get_object_or_404(Resource, pk=kwargs['pk'])
if not self.resource.can_be_viewed(request.user):
raise PermissionDenied()
try:
self.date = make_aware(
datetime.datetime.strptime(
'%s-%s-%s 06:00' % (self.get_year(), self.get_month(), 1), '%Y-%m-%d %H:%M'
)
)
except ValueError:
raise Http404
return super().dispatch(request, *args, **kwargs)
class ResourceWeekMonthMixin:
def get_queryset(self):
queryset = (
self.resource.event_set.all()
@ -418,23 +401,19 @@ class ResourceMonthView(DateMixin, MonthArchiveView):
context = super().get_context_data(**kwargs)
context['resource'] = self.resource
context['kind'] = self.kind
context['hour_span'] = 1
durations = MeetingType.objects.filter(agenda__resources=self.resource).values_list(
'duration', flat=True
)
if durations:
gcd = durations[0]
for duration in durations[1:]:
gcd = math.gcd(duration, gcd)
context['hour_span'] = max(60 // gcd, 1)
return context
def get_previous_month_url(self):
previous_month = self.get_previous_month(self.date.date())
return reverse(
'chrono-manager-resource-month-view',
kwargs={'pk': self.resource.pk, 'year': previous_month.year, 'month': previous_month.month},
)
def get_next_month_url(self):
next_month = self.get_next_month(self.date.date())
return reverse(
'chrono-manager-resource-month-view',
kwargs={'pk': self.resource.pk, 'year': next_month.year, 'month': next_month.month},
)
def get_timetable_infos(self):
interval = datetime.timedelta(minutes=60)
@ -469,18 +448,20 @@ class ResourceMonthView(DateMixin, MonthArchiveView):
hide_weekend = hide_weekend_timeperiod and hide_weekend_event
# avoid displaying empty first week
first_week_offset = int(
(hide_sunday and self.date.weekday() == 6) or (hide_weekend and self.date.weekday() == 5)
)
first_week_offset = 0
first_week_number = self.date.isocalendar()[1]
if first_week_number >= 52:
first_week_number = 0
last_month_day = self.get_next_month(self.date.date()) - datetime.timedelta(days=1)
last_week_number = last_month_day.isocalendar()[1]
last_week_number = first_week_number
if self.kind == 'month':
first_week_offset = int(
(hide_sunday and self.date.weekday() == 6) or (hide_weekend and self.date.weekday() == 5)
)
if first_week_number >= 52:
first_week_number = 0
last_day = self.get_next(self.date.date()) - datetime.timedelta(days=1)
last_week_number = last_day.isocalendar()[1]
if last_week_number < first_week_number: # new year
last_week_number = 53
if last_week_number < first_week_number: # new year
last_week_number = 53
for week_number in range(first_week_number + first_week_offset, last_week_number + 1):
yield self.get_week_timetable_infos(
@ -528,8 +509,8 @@ class ResourceMonthView(DateMixin, MonthArchiveView):
# until the end of the last hour.
max_date += datetime.timedelta(hours=1)
# compute booking and opening hours only for current month
if self.date.month != day.month:
# compute booking and opening hours only for current month/week
if self.kind == 'month' and timetable['other_month']:
return timetable
while period <= max_date:
@ -552,6 +533,92 @@ class ResourceMonthView(DateMixin, MonthArchiveView):
return timetable
class ResourceWeekView(ResourceWeekMonthMixin, DateMixin, WeekArchiveView):
template_name = 'chrono/manager_resource_week_view.html'
model = Event
week_format = '%W'
date_field = 'start_datetime'
allow_empty = True
allow_future = True
kind = 'week'
def dispatch(self, request, *args, **kwargs):
self.resource = get_object_or_404(Resource, pk=kwargs['pk'])
if not self.resource.can_be_viewed(request.user):
raise PermissionDenied()
try:
date = datetime.datetime.strptime('%s-W%s-1' % (self.get_year(), self.get_week()), "%Y-W%W-%w")
self.date = make_aware(
datetime.datetime.strptime(
'%s-%s-%s 06:00' % (self.get_year(), date.month, date.day), '%Y-%m-%d %H:%M'
)
)
except ValueError:
raise Http404
return super().dispatch(request, *args, **kwargs)
def get_previous_week_url(self):
previous_week = self.get_previous_week(self.date.date())
return reverse(
'chrono-manager-resource-week-view',
kwargs={'pk': self.resource.pk, 'year': previous_week.year, 'week': previous_week.strftime('%W')},
)
def get_next_week_url(self):
next_week = self.get_next_week(self.date.date())
return reverse(
'chrono-manager-resource-week-view',
kwargs={'pk': self.resource.pk, 'year': next_week.year, 'week': next_week.strftime('%W')},
)
def get_next(self, date):
return self.get_next_week(date)
resource_weekly_view = ResourceWeekView.as_view()
class ResourceMonthView(ResourceWeekMonthMixin, DateMixin, MonthArchiveView):
template_name = 'chrono/manager_resource_month_view.html'
model = Event
month_format = '%m'
date_field = 'start_datetime'
allow_empty = True
allow_future = True
kind = 'month'
def dispatch(self, request, *args, **kwargs):
self.resource = get_object_or_404(Resource, pk=kwargs['pk'])
if not self.resource.can_be_viewed(request.user):
raise PermissionDenied()
try:
self.date = make_aware(
datetime.datetime.strptime(
'%s-%s-%s 06:00' % (self.get_year(), self.get_month(), 1), '%Y-%m-%d %H:%M'
)
)
except ValueError:
raise Http404
return super().dispatch(request, *args, **kwargs)
def get_previous_month_url(self):
previous_month = self.get_previous_month(self.date.date())
return reverse(
'chrono-manager-resource-month-view',
kwargs={'pk': self.resource.pk, 'year': previous_month.year, 'month': previous_month.month},
)
def get_next_month_url(self):
next_month = self.get_next_month(self.date.date())
return reverse(
'chrono-manager-resource-month-view',
kwargs={'pk': self.resource.pk, 'year': next_month.year, 'month': next_month.month},
)
def get_next(self, date):
return self.get_next_month(date)
resource_monthly_view = ResourceMonthView.as_view()
@ -1082,6 +1149,9 @@ class AgendaView(ViewableAgendaMixin, View):
if self.agenda.default_view == 'day':
return redirect('chrono-manager-agenda-day-redirect-view', pk=self.agenda.pk)
if self.agenda.default_view == 'week':
return redirect('chrono-manager-agenda-week-redirect-view', pk=self.agenda.pk)
if self.agenda.default_view == 'month':
return redirect('chrono-manager-agenda-month-redirect-view', pk=self.agenda.pk)
@ -1114,6 +1184,17 @@ class AgendaMonthRedirectView(ViewableAgendaMixin, View):
agenda_month_redirect_view = AgendaMonthRedirectView.as_view()
class AgendaWeekRedirectView(AgendaMonthRedirectView):
def get(self, request, *args, **kwargs):
day = self.get_day()
return redirect(
'chrono-manager-agenda-week-view', pk=self.agenda.pk, year=day.year, week=day.strftime('%W')
)
agenda_week_redirect_view = AgendaWeekRedirectView.as_view()
class AgendaDayRedirectView(AgendaMonthRedirectView):
def get(self, request, *args, **kwargs):
day = self.get_day()
@ -1128,6 +1209,7 @@ agenda_day_redirect_view = AgendaDayRedirectView.as_view()
class AgendaDateView(DateMixin, ViewableAgendaMixin):
model = Event
month_format = '%m'
week_format = '%W'
date_field = 'start_datetime'
allow_empty = True
allow_future = True
@ -1333,7 +1415,7 @@ class AgendaDayView(AgendaDateView, DayArchiveView):
agenda_day_view = AgendaDayView.as_view()
class AgendaMonthView(AgendaDateView, MonthArchiveView):
class AgendaWeekMonthMixin:
def get_queryset(self):
qs = super().get_queryset()
if self.agenda.kind != 'events':
@ -1343,9 +1425,9 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
def get_dated_items(self):
date_list, object_list, extra_context = super().get_dated_items()
if self.agenda.kind == 'events':
min_start = make_aware(datetime.datetime.combine(extra_context['month'], datetime.time(0, 0)))
min_start = make_aware(datetime.datetime.combine(extra_context[self.kind], datetime.time(0, 0)))
max_start = make_aware(
datetime.datetime.combine(extra_context['next_month'], datetime.time(0, 0))
datetime.datetime.combine(extra_context['next_%s' % self.kind], datetime.time(0, 0))
)
exceptions = TimePeriodException.objects.filter(
desk__agenda=self.agenda, start_datetime__gte=min_start, end_datetime__lt=max_start
@ -1353,11 +1435,6 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
object_list = sorted(itertools.chain(object_list, exceptions), key=lambda x: x.start_datetime)
return date_list, object_list, extra_context
def get_template_names(self):
if self.agenda.kind == 'virtual':
return ['chrono/manager_meetings_agenda_month_view.html']
return ['chrono/manager_%s_agenda_month_view.html' % self.agenda.kind]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.agenda.kind == 'events':
@ -1367,25 +1444,9 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
).all()
else:
context['single_desk'] = bool(len(self.agenda.prefetched_desks) == 1)
context['kind'] = self.kind
return context
def get_previous_month_url(self):
previous_month = self.get_previous_month(self.date.date())
return reverse(
'chrono-manager-agenda-month-view',
kwargs={'pk': self.agenda.id, 'year': previous_month.year, 'month': previous_month.month},
)
def get_next_month_url(self):
next_month = self.get_next_month(self.date.date())
return reverse(
'chrono-manager-agenda-month-view',
kwargs={'pk': self.agenda.id, 'year': next_month.year, 'month': next_month.month},
)
def get_day(self):
return '1'
def get_timetable_infos(self):
timeperiods = itertools.chain(*(d.timeperiod_set.all() for d in self.agenda.prefetched_desks))
timeperiods = sorted(timeperiods, key=lambda t: (t.weekday, t.start_time))
@ -1419,18 +1480,21 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
hide_weekend = hide_weekend_timeperiod and hide_weekend_event
# avoid displaying empty first week
first_week_offset = int(
(hide_sunday and self.date.weekday() == 6) or (hide_weekend and self.date.weekday() == 5)
)
first_week_offset = 0
first_week_number = self.date.isocalendar()[1]
if first_week_number >= 52:
first_week_number = 0
last_month_day = self.get_next_month(self.date.date()) - datetime.timedelta(days=1)
last_week_number = last_month_day.isocalendar()[1]
last_week_number = first_week_number
if self.kind == 'month':
first_week_offset = int(
(hide_sunday and self.date.weekday() == 6) or (hide_weekend and self.date.weekday() == 5)
)
first_week_number = self.date.isocalendar()[1]
if first_week_number >= 52:
first_week_number = 0
last_day = self.get_next(self.date.date()) - datetime.timedelta(days=1)
last_week_number = last_day.isocalendar()[1]
if last_week_number < first_week_number: # new year
last_week_number = 53
if last_week_number < first_week_number: # new year
last_week_number = 53
for week_number in range(first_week_number + first_week_offset, last_week_number + 1):
yield self.get_week_timetable_infos(
@ -1474,8 +1538,8 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
desks_len = len(desks)
max_date = day.replace(hour=self.max_display.hour, minute=0)
# compute booking and opening hours only for current month
if self.date.month != day.month:
# compute booking and opening hours only for current month/week
if self.kind == 'month' and timetable['other_month']:
return timetable
while period <= max_date:
@ -1536,6 +1600,73 @@ class AgendaMonthView(AgendaDateView, MonthArchiveView):
return timetable
class AgendaWeekView(AgendaWeekMonthMixin, AgendaDateView, WeekArchiveView):
week_format = "%W"
kind = 'week'
def get_template_names(self):
if self.agenda.kind == 'virtual':
return ['chrono/manager_meetings_agenda_week_view.html']
return ['chrono/manager_%s_agenda_week_view.html' % self.agenda.kind]
def get_previous_week_url(self):
previous_week = self.get_previous_week(self.date.date())
return reverse(
'chrono-manager-agenda-week-view',
kwargs={'pk': self.agenda.id, 'year': previous_week.year, 'week': previous_week.strftime('%W')},
)
def get_next_week_url(self):
next_week = self.get_next_week(self.date.date())
return reverse(
'chrono-manager-agenda-week-view',
kwargs={'pk': self.agenda.id, 'year': next_week.year, 'week': next_week.strftime('%W')},
)
def get_next(self, date):
return self.get_next_week(date)
def get_month(self):
date = datetime.datetime.strptime('%s-W%s-1' % (self.get_year(), self.get_week()), "%Y-W%W-%w")
return date.month
def get_day(self):
date = datetime.datetime.strptime('%s-W%s-1' % (self.get_year(), self.get_week()), "%Y-W%W-%w")
return date.day
agenda_weekly_view = AgendaWeekView.as_view()
class AgendaMonthView(AgendaWeekMonthMixin, AgendaDateView, MonthArchiveView):
kind = 'month'
def get_template_names(self):
if self.agenda.kind == 'virtual':
return ['chrono/manager_meetings_agenda_month_view.html']
return ['chrono/manager_%s_agenda_month_view.html' % self.agenda.kind]
def get_previous_month_url(self):
previous_month = self.get_previous_month(self.date.date())
return reverse(
'chrono-manager-agenda-month-view',
kwargs={'pk': self.agenda.id, 'year': previous_month.year, 'month': previous_month.month},
)
def get_next_month_url(self):
next_month = self.get_next_month(self.date.date())
return reverse(
'chrono-manager-agenda-month-view',
kwargs={'pk': self.agenda.id, 'year': next_month.year, 'month': next_month.month},
)
def get_next(self, date):
return self.get_next_month(date)
def get_day(self):
return '1'
agenda_monthly_view = AgendaMonthView.as_view()

View File

@ -127,6 +127,12 @@ def test_events_agenda_redirect(app, admin_user):
resp = app.get('/manage/agendas/%s/' % agenda_id, status=302)
assert resp.location.endswith('/manage/agendas/%s/day/' % agenda.pk)
agenda.default_view = 'week'
agenda.save()
for agenda_id in [agenda.pk, agenda.slug]:
resp = app.get('/manage/agendas/%s/' % agenda_id, status=302)
assert resp.location.endswith('/manage/agendas/%s/week/' % agenda.pk)
@freezegun.freeze_time('2020-07-12')
def test_events_agenda_month_redirect(app, admin_user):
@ -180,6 +186,58 @@ def test_events_agenda_month_redirect(app, admin_user):
assert resp.location.endswith('/manage/agendas/%s/2020/7/' % agenda.pk)
@freezegun.freeze_time('2020-07-12')
def test_events_agenda_week_redirect(app, admin_user):
agenda = Agenda.objects.create(label='Foo Bar', kind='events')
app = login(app)
# no event, redirect to current week
resp = app.get('/manage/agendas/%s/week/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/week/27/' % agenda.pk)
# only past events, redirect to last event month
Event.objects.create(
agenda=agenda,
places=1,
start_datetime=now() - datetime.timedelta(days=60),
)
resp = app.get('/manage/agendas/%s/week/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/week/19/' % agenda.pk)
Event.objects.create(
agenda=agenda,
places=1,
start_datetime=now() - datetime.timedelta(days=30),
)
resp = app.get('/manage/agendas/%s/week/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/week/23/' % agenda.pk)
# future events
Event.objects.create(
agenda=agenda,
places=1,
start_datetime=now() + datetime.timedelta(days=60),
)
resp = app.get('/manage/agendas/%s/week/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/week/36/' % agenda.pk)
Event.objects.create(
agenda=agenda,
places=1,
start_datetime=now() + datetime.timedelta(days=30),
)
resp = app.get('/manage/agendas/%s/week/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/week/32/' % agenda.pk)
# don't check events for meetings
agenda.kind = 'virtual'
agenda.save()
resp = app.get('/manage/agendas/%s/week/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/week/27/' % agenda.pk)
agenda.kind = 'meetings'
agenda.save()
resp = app.get('/manage/agendas/%s/week/' % agenda.pk, status=302)
assert resp.location.endswith('/manage/agendas/%s/2020/week/27/' % agenda.pk)
@freezegun.freeze_time('2020-07-12')
def test_events_agenda_day_redirect(app, admin_user):
agenda = Agenda.objects.create(label='Foo Bar', kind='events')
@ -246,6 +304,12 @@ def test_meetings_agenda_redirect(app, admin_user):
resp = app.get('/manage/agendas/%s/' % agenda_id, status=302)
assert resp.location.endswith('/manage/agendas/%s/month/' % agenda.pk)
agenda.default_view = 'week'
agenda.save()
for agenda_id in [agenda.pk, agenda.slug]:
resp = app.get('/manage/agendas/%s/' % agenda_id, status=302)
assert resp.location.endswith('/manage/agendas/%s/week/' % agenda.pk)
def test_virtual_agenda_redirect(app, admin_user):
agenda = Agenda.objects.create(label='Foo Bar', kind='virtual')
@ -261,6 +325,12 @@ def test_virtual_agenda_redirect(app, admin_user):
resp = app.get('/manage/agendas/%s/' % agenda_id, status=302)
assert resp.location.endswith('/manage/agendas/%s/month/' % agenda.pk)
agenda.default_view = 'week'
agenda.save()
for agenda_id in [agenda.pk, agenda.slug]:
resp = app.get('/manage/agendas/%s/' % agenda_id, status=302)
assert resp.location.endswith('/manage/agendas/%s/week/' % agenda.pk)
def test_view_agendas_as_admin(app, admin_user):
Agenda.objects.create(label='Bar Foo')
@ -929,10 +999,11 @@ def test_agenda_day_view(app, admin_user, manager_user, api_user, get_proper_htm
'view',
(
'/manage/agendas/%(agenda)s/%(year)d/%(month)d/%(day)d/',
'/manage/agendas/%(agenda)s/%(year)d/week/%(week)d/',
'/manage/agendas/%(agenda)s/%(year)d/%(month)d/',
),
)
def test_agenda_day_month_view_backoffice_url_translation(
def test_agenda_day_week_month_view_backoffice_url_translation(
app, admin_user, manager_user, api_user, settings, view
):
agenda = Agenda.objects.create(label='New Example', kind='meetings')
@ -959,7 +1030,13 @@ def test_agenda_day_month_view_backoffice_url_translation(
booking = Booking.objects.get(pk=booking_id)
assert booking.backoffice_url == backoffice_url
date = booking.event.start_datetime
url = view % {'agenda': agenda.id, 'year': date.year, 'month': date.month, 'day': date.day}
url = view % {
'agenda': agenda.id,
'year': date.year,
'month': date.month,
'week': int(date.strftime('%W')),
'day': date.day,
}
resp = app.get(url)
assert resp.text.count('div class="booking') == 1
assert backoffice_url in resp.text
@ -1139,6 +1216,97 @@ def test_agenda_events_day_view_midnight(app, admin_user):
assert len(resp.pyquery.find('.event-info')) == 1
@freezegun.freeze_time('2020-10-01')
def test_agenda_events_week_view(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
today = datetime.date.today()
login(app)
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.pk, today.year, today.strftime('%W')))
assert 'Day view' in resp.text
assert "This week doesn't have any event configured." in resp.text
# add event in a future month, a wednesday
Event.objects.create(
label='xyz', start_datetime=localtime().replace(day=11, month=11, year=2020), places=10, agenda=agenda
)
# current month still doesn't have events
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
assert "This week doesn't have any event configured." in resp.text
# add recurring event on every Wednesday
start_datetime = localtime().replace(day=4, month=11, year=2020)
event = Event.objects.create(
label='abc',
start_datetime=start_datetime,
places=10,
agenda=agenda,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=60),
)
event.create_all_recurrences()
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, 2020, 45))
assert len(ctx.captured_queries) == 7
assert len(resp.pyquery.find('.event-info')) == 2
assert 'abc' in resp.pyquery.find('.event-info')[0].text
assert 'xyz' in resp.pyquery.find('.event-info')[1].text
TimePeriodException.objects.create(
desk=agenda.desk_set.get(),
start_datetime=start_datetime + datetime.timedelta(days=6),
end_datetime=start_datetime + datetime.timedelta(days=8),
)
agenda.update_event_recurrences()
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, 2020, 45))
assert len(resp.pyquery.find('.event-info')) == 1
assert 'Exception: 11/10/2020' in resp.pyquery.find('li')[4].text_content()
assert 'xyz' in resp.pyquery.find('li')[5].text_content()
# create another event with recurrence, the same day/time
start_datetime = localtime().replace(day=4, month=11, year=2020)
event = Event.objects.create(
label='def',
start_datetime=start_datetime,
places=10,
agenda=agenda,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=60),
)
event.create_all_recurrences()
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, 2020, 49))
assert len(resp.pyquery.find('.event-info')) == 2
time = localtime(event.start_datetime).strftime('%H%M')
resp = resp.click('Dec. 9, 2020, 1 a.m.', index=1)
resp = resp.click('Options')
resp.form['start_datetime_1'] = '13:12'
resp = resp.form.submit(status=302).follow()
agenda.update_event_recurrences()
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, 2020, 49))
assert len(resp.pyquery.find('.event-info')) == 2
assert '1:12 p.m.' in resp.text
Event.objects.get(slug='abc--2020-12-02-%s' % time).cancel()
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, 2020, 48))
assert 'Cancelled' in resp.text
def test_agenda_events_week_view_midnight(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events', default_view='day')
midnight = localtime(now().replace(day=1, month=11, year=2020)).replace(hour=0, minute=0)
Event.objects.create(label='xyz', start_datetime=midnight, places=10, agenda=agenda)
login(app)
resp = app.get('/manage/agendas/%s/week/' % agenda.pk)
assert resp.location.endswith('/2020/week/43/')
resp = resp.follow()
assert len(resp.pyquery.find('.event-info')) == 1
@freezegun.freeze_time('2020-10-01')
def test_agenda_events_month_view(app, admin_user):
agenda = Agenda.objects.create(label='Events', kind='events')
@ -1365,7 +1533,8 @@ def test_agenda_month_view(app, admin_user, manager_user, api_user, get_proper_h
resp = resp.click('Month view')
assert resp.request.url.endswith('%s/%s/' % (today.year, today.month))
assert 'Day view' in resp.text # date view link should be present
assert 'Day view' in resp.text # day view link should be present
assert 'Week view' in resp.text # week view link should be present
assert 'No opening hours this month.' in resp.text
today = datetime.date(2018, 11, 10) # fixed day
@ -1744,6 +1913,383 @@ def test_agenda_month_view_event_outside_timeperiod(app, admin_user, kind):
assert 'Saturday' in resp.text
def test_agenda_week_view(app, admin_user, manager_user, api_user, get_proper_html_str):
agenda = Agenda.objects.create(label='Passeports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
today = datetime.date.today()
meetingtype = MeetingType(agenda=agenda, label='passeport', duration=20)
meetingtype.save()
login(app)
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.pk, today.year, today.month, today.day))
assert 'Week view' in resp.text
resp = resp.click('Week view')
assert resp.request.url.endswith('%s/week/%s/' % (today.year, today.strftime('%W')))
assert 'Day view' in resp.text # day view link should be present
assert 'Month view' in resp.text # month view link should be present
assert 'No opening hours this week.' in resp.text
today = datetime.date(2018, 11, 10) # fixed day
timeperiod_weekday = today.weekday()
timeperiod = TimePeriod(
desk=desk, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
timeperiod.save()
app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, today.year, 72), status=404)
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
assert 'No opening hours this week.' not in resp.text
assert '<div class="booking' not in resp.text
assert resp.text.count('<tr') == 9
# check opening hours cells
assert '<div class="opening-hours" style="height:800.0%;top:0.0%;width:97.0%;left:1.0%' in resp.text
# book some slots
app.reset()
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
booking_url = resp.json['data'][0]['api']['fillslot_url']
booking_url2 = resp.json['data'][2]['api']['fillslot_url']
booking = app.post(booking_url)
booking_2 = app.post_json(
booking_url2, params={'label': 'foo book', 'user_last_name': "bar's", 'url': 'http://baz/'}
)
app.reset()
login(app)
date = Booking.objects.all()[0].event.start_datetime
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, date.year, date.strftime('%W')))
assert resp.text.count('<div class="booking" style="left:1.0%;height:33.0%;') == 2 # booking cells
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo book - bar's"
assert get_proper_html_str('foo book - bar&#39;s') in resp
assert len(resp.pyquery.find('span.desk')) == 0
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
agenda.save()
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, date.year, date.strftime('%W')))
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
assert get_proper_html_str('&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar') in resp
desk = Desk.objects.create(agenda=agenda, label='Desk B')
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, date.year, date.strftime('%W')))
assert len(resp.pyquery.find('span.desk')) == 2
timeperiod = TimePeriod(
desk=desk, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
timeperiod.save()
app.reset()
booking_3 = app.post(booking_url)
login(app)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
# count occurences of timeperiod weekday in current month
assert resp.text.count('<div class="opening-hours"') == 2
current_month = today.strftime('%Y-%m')
if current_month in booking_url or current_month in booking_url2:
assert resp.text.count('<div class="booking"') == 3
# cancel bookings
app.reset()
app.post(booking.json['api']['cancel_url'])
app.post(booking_2.json['api']['cancel_url'])
app.post(booking_3.json['api']['cancel_url'])
# make sure the are not
login(app)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
assert resp.text.count('<div class="booking"') == 0
# check December is correctly displayed
today = datetime.date(2018, 12, 10)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
assert 'No opening hours this week.' not in resp.text
# display exception
unavailability_calendar = UnavailabilityCalendar.objects.create(label='calendar')
TimePeriodException.objects.create(
label='Calendar exception',
unavailability_calendar=unavailability_calendar,
start_datetime=make_aware(datetime.datetime(2018, 12, 15, 5, 0)),
end_datetime=make_aware(datetime.datetime(2018, 12, 15, 14, 0)),
)
unavailability_calendar.desks.add(desk)
TimePeriodException.objects.create(
label='Exception for a December day',
desk=desk,
start_datetime=make_aware(datetime.datetime(2018, 12, 15, 14, 0)),
end_datetime=make_aware(datetime.datetime(2018, 12, 15, 23, 0)),
)
TimePeriodException.objects.create(
label='Exception spanning multiple days',
desk=desk,
start_datetime=make_aware(datetime.datetime(2018, 12, 11, 14, 0)),
end_datetime=make_aware(datetime.datetime(2018, 12, 13, 16, 0)),
)
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
assert len(ctx.captured_queries) == 10
assert resp.pyquery.find('.exception-hours')[0].attrib == {
'class': 'exception-hours',
'style': 'height:400.0%;top:400.0%;width:48.0%;left:50.0%;',
'title': 'Exception spanning multiple days',
}
assert resp.pyquery.find('.exception-hours')[1].attrib == {
'class': 'exception-hours',
'style': 'height:800.0%;top:0.0%;width:48.0%;left:50.0%;',
'title': 'Exception spanning multiple days',
}
assert resp.pyquery.find('.exception-hours')[2].attrib == {
'class': 'exception-hours',
'style': 'height:600.0%;top:0.0%;width:48.0%;left:50.0%;',
'title': 'Exception spanning multiple days',
}
assert resp.pyquery.find('.exception-hours')[3].attrib == {
'class': 'exception-hours',
'style': 'height:400.0%;top:0.0%;width:48.0%;left:50.0%;',
'title': 'Calendar exception',
}
assert resp.pyquery.find('.exception-hours')[4].attrib == {
'class': 'exception-hours',
'style': 'height:400.0%;top:400.0%;width:48.0%;left:50.0%;',
'title': 'Exception for a December day',
}
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_week_view_weekend(app, admin_user, kind):
week, year = '02', 2019
monday = 0
if kind == 'meetings':
agenda = Agenda.objects.create(label='Passeports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real 1', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
TimePeriod.objects.create(
desk=desk, weekday=monday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
login(app)
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, year, week))
assert 'Sunday' not in resp.text
assert 'Saturday' not in resp.text
# Month starts a Tuesday, but monday is displayed
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
week, year = 21, 2019 # month starts a Saturday
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, year, week))
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
saturday = 5
timeperiod_sat = TimePeriod.objects.create(
desk=desk, weekday=saturday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, year, week))
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
sunday = 6
TimePeriod.objects.create(
desk=desk, weekday=sunday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, year, week))
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
timeperiod_sat.delete()
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, year, week))
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_week_view_dst_change(app, admin_user, kind):
if kind == 'meetings':
agenda = Agenda.objects.create(label='Passeports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
meetingtype = MeetingType.objects.create(agenda=agenda, label='passeport', duration=20)
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real 1', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=real_agenda, label='passeport', duration=20)
for weekday in range(0, 7): # open all mornings
TimePeriod.objects.create(
desk=desk, weekday=weekday, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
)
login(app)
for date in ('2019-10-28', '2019-11-03'):
# dst change was on 2019-11-03
with freezegun.freeze_time(date):
resp = app.get('/manage/agendas/%s/2019/week/43/' % agenda.id)
# check all days are correctly aligned
assert resp.text.count('height:300.0%;top:0.0%') == 7
# book some slots
for date_tuple in [(2019, 10, 29, 10, 0), (2019, 10, 30, 10, 0)]:
event = Event.objects.create(
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(make_aware(datetime.datetime(*date_tuple))),
)
Booking.objects.create(event=event)
# check booked slots are similarly aligned
resp = app.get('/manage/agendas/%s/2019/week/43/' % agenda.id)
assert resp.text.count('height:33.0%;top:100.0%;') == 2
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_week_view_januaries(app, admin_user, kind):
if kind == 'meetings':
agenda = Agenda.objects.create(label='Passports', kind='meetings')
desk = Desk.objects.create(agenda=agenda, label='Desk A')
MeetingType.objects.create(agenda=agenda, label='passport', duration=20)
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real 1', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
MeetingType.objects.create(agenda=real_agenda, label='passport', duration=20)
TimePeriod(desk=desk, weekday=2, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)).save()
for year in range(2020, 2030):
date = datetime.date(year, 1, 1)
with freezegun.freeze_time(date):
login(app)
resp = app.get('/manage/agendas/%s/%s/week/01/' % (agenda.id, date.year))
assert resp.text.count('<th></th>') == 1
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
def test_agenda_week_view_event_outside_timeperiod(app, admin_user, kind):
middle_day = now().replace(day=15)
middle_day = middle_day + datetime.timedelta(days=4 - middle_day.weekday())
if kind == 'meetings':
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)
else:
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda = Agenda.objects.create(label='Real 1', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda)
desk = Desk.objects.create(agenda=real_agenda, label='New Desk')
meetingtype = MeetingType.objects.create(agenda=real_agenda, label='passport', duration=20)
login(app)
# no time period - no events
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert 'No opening hours this week.' in resp.text
assert 'div class="booking' not in resp.text
# book some slots
middle_day = now().replace(day=15)
for hour, minute in [(9, 0), (17, 0)]:
event = Event.objects.create(
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now().replace(day=middle_day.day - middle_day.weekday() + 2)).replace(
hour=hour, minute=minute
),
)
Booking.objects.create(event=event)
# no time period - events are displayed
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert resp.text.count('div class="booking') == 2
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert 'No opening hours this week.' in resp.text
assert resp.text.count('div class="booking') == 0
# events outside time period
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=2, start_time=datetime.time(10, 0), end_time=datetime.time(16, 0)
)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert resp.text.count('div class="booking') == 2
assert '<div class="opening-hours" style="height:600.0%;top:100.0%;width:97.0%;left:1.0%' in resp.text
assert 'Sunday' not in resp.text
assert 'Saturday' not in resp.text
# create an event on saturday
middle_day = now().replace(day=15)
middle_day = middle_day + datetime.timedelta(days=5 - middle_day.weekday())
event = Event.objects.create(
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(day=middle_day.day, hour=10, minute=0),
)
Booking.objects.create(event=event)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert resp.text.count('div class="booking') == 3
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert resp.text.count('div class="booking') == 0
# and a timeperiod
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=5, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0)
)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert resp.text.count('div class="booking') == 3
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
# create an event on sunday
middle_day = now().replace(day=15)
middle_day = middle_day + datetime.timedelta(days=6 - middle_day.weekday())
event = Event.objects.create(
agenda=desk.agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(day=middle_day.day, hour=10, minute=0),
)
Booking.objects.create(event=event)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert resp.text.count('div class="booking') == 4
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert resp.text.count('div class="booking') == 0
# and a timeperiod
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=6, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0)
)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.pk, middle_day.year, middle_day.strftime('%W')))
assert resp.text.count('div class="booking') == 4
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
def test_agenda_view_event(app, manager_user):
agenda = Agenda(label='Foo bar')
agenda.view_role = manager_user.groups.all()[0]
@ -1997,6 +2543,129 @@ def test_virtual_agenda_day_view(app, admin_user, manager_user, get_proper_html_
assert 'exceptions-hours' not in resp.text
def test_virtual_agenda_week_view(app, admin_user, get_proper_html_str):
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda_1 = Agenda.objects.create(label='Real 1', kind='meetings')
real_agenda_2 = Agenda.objects.create(label='Real 2', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda_1)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=real_agenda_2)
desk1 = Desk.objects.create(agenda=real_agenda_1, label='New Desk')
desk2 = Desk.objects.create(agenda=real_agenda_2, label='New Desk')
today = datetime.date.today()
meetingtype1 = MeetingType.objects.create(agenda=real_agenda_1, label='Bar', duration=30)
meetingtype2 = MeetingType.objects.create(agenda=real_agenda_2, label='Bar', duration=30)
login(app)
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.pk, today.year, today.month, today.day))
assert 'Week view' in resp.text
resp = resp.click('Week view')
assert resp.request.url.endswith('%s/week/%s/' % (today.year, today.strftime('%W')))
assert 'Day view' in resp.text # day view link should be present
assert 'Month view' in resp.text # month view link should be present
assert 'No opening hours this week.' in resp.text
today = datetime.date(2018, 11, 10) # fixed day
timeperiod_weekday = today.weekday()
TimePeriod.objects.create(
desk=desk1, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/agendas/%s/%s/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
assert 'No opening hours this week.' not in resp.text
assert '<div class="booking' not in resp.text
assert resp.text.count('<tr') == 9
# check opening hours cells
assert '<div class="opening-hours" style="height:800.0%;top:0.0%;width:48.0%;left:1.0%' in resp.text
# book some slots
for hour, minute in [(10, 30), (14, 0)]:
event = Event.objects.create(
agenda=real_agenda_1,
places=1,
desk=desk1,
meeting_type=meetingtype1,
start_datetime=now().replace(hour=hour, minute=minute),
)
Booking.objects.create(event=event)
event = Event.objects.create(
agenda=real_agenda_2,
places=1,
desk=desk2,
meeting_type=meetingtype2,
start_datetime=now().replace(hour=hour, minute=minute),
)
Booking.objects.create(event=event, label='foo', user_last_name="bar's")
date = Booking.objects.all()[0].event.start_datetime
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, date.year, date.strftime('%W')))
assert resp.text.count('<div class="booking" style="left:1.0%;height:50.0%;') == 2 # booking cells
assert (
resp.text.count('<div class="booking" style="left:50.0%;height:50.0%;min-height:50.0%;') == 2
) # booking cells
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "foo - bar's"
assert get_proper_html_str('foo - bar&#39;s') in resp
real_agenda_1.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
real_agenda_1.save()
real_agenda_2.booking_user_block_template = '<b>{{ booking.user_name }}</b> Bar Foo'
real_agenda_2.save()
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Bar Foo"
assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "<b>bar's</b> Bar Foo"
assert get_proper_html_str('&lt;b&gt;bar&#39;s&lt;/b&gt; Bar Foo') in resp
# cancel a booking
booking = Booking.objects.first()
booking.cancel()
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, date.year, date.strftime('%W')))
assert resp.text.count('<div class="booking"') == 3
# check December is correctly displayed
today = datetime.date(2018, 12, 10)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
assert 'No opening hours this month.' not in resp.text
# display exception
TimePeriodException.objects.create(
label='Exception for a December day',
desk=desk1,
start_datetime=make_aware(datetime.datetime(2018, 12, 15, 5, 0)),
end_datetime=make_aware(datetime.datetime(2018, 12, 15, 23, 0)),
)
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
assert len(ctx.captured_queries) == 12
assert len(resp.pyquery.find('.exception-hours')) == 1
assert resp.pyquery.find('.exception-hours')[0].attrib == {
'class': 'exception-hours',
'style': 'height:800.0%;top:0.0%;width:48.0%;left:1.0%;',
'title': 'Exception for a December day',
}
# display excluded period
TimePeriod.objects.create(
agenda=agenda, weekday=today.weekday(), start_time=datetime.time(13, 0), end_time=datetime.time(23, 0)
)
resp = app.get('/manage/agendas/%s/%d/week/%s/' % (agenda.id, today.year, today.strftime('%W')))
assert len(resp.pyquery.find('.exception-hours')) == 3
assert resp.pyquery.find('.exception-hours')[0].attrib == {
'class': 'exception-hours',
'style': 'height:500.0%;top:300.0%;width:48.0%;left:1.0%;',
}
assert resp.pyquery.find('.exception-hours')[1].attrib == {
'class': 'exception-hours',
'style': 'height:500.0%;top:300.0%;width:48.0%;left:50.0%;',
}
def test_virtual_agenda_month_view(app, admin_user, get_proper_html_str):
agenda = Agenda.objects.create(label='Virtual', kind='virtual')
real_agenda_1 = Agenda.objects.create(label='Real 1', kind='meetings')
@ -2017,7 +2686,8 @@ def test_virtual_agenda_month_view(app, admin_user, get_proper_html_str):
resp = resp.click('Month view')
assert resp.request.url.endswith('%s/%s/' % (today.year, today.month))
assert 'Day view' in resp.text # date view link should be present
assert 'Day view' in resp.text # day view link should be present
assert 'Week view' in resp.text # week view link should be present
assert 'No opening hours this month.' in resp.text
today = datetime.date(2018, 11, 10) # fixed day

View File

@ -220,6 +220,296 @@ def test_day_view_resource_as_manager(app, manager_user):
app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day), status=403)
@freezegun.freeze_time('2020-06-15')
def test_resource_week_view(app, admin_user, get_proper_html_str):
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=20)
TimePeriod.objects.create(
desk=desk, weekday=0, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
login(app)
today = datetime.date(2018, 11, 10) # fixed day
app.get('/manage/resource/%s/%s/%s/' % (resource.pk, today.year, 72), status=404)
resp = app.get('/manage/resource/%s/%s/week/%s/' % (resource.pk, today.year, today.strftime('%W')))
assert '<div class="booking' not in resp.text
assert resp.text.count('<tr') == 9
# book some slots
for hour, minute in [(10, 30), (14, 0)]:
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=now().replace(hour=hour, minute=minute),
)
event.resources.add(resource)
if hour == 10:
Booking.objects.create(event=event)
else:
Booking.objects.create(event=event, label='foo', user_last_name="bar's")
today = datetime.date.today()
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/resource/%s/%s/week/%s/' % (resource.pk, today.year, today.strftime('%W')))
assert len(ctx.captured_queries) == 8
assert resp.text.count('<div class="booking" style="height:33.0%;') == 2 # booking cells
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
assert get_proper_html_str('foo - bar&#39;s') in resp
agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
agenda.save()
resp = app.get('/manage/resource/%s/%s/week/%s/' % (resource.pk, today.year, today.strftime('%W')))
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
assert get_proper_html_str('&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar') in resp
# cancel booking
booking = Booking.objects.first()
booking.cancel()
# make sure the are not
resp = app.get('/manage/resource/%s/%s/week/%s/' % (resource.pk, today.year, today.strftime('%W')))
assert resp.text.count('<div class="booking"') == 1
def test_resource_week_view_weekend(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
monday = 0
TimePeriod.objects.create(
desk=desk, weekday=monday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
week, year = '01', 2019
login(app)
resp = app.get('/manage/resource/%s/%s/week/%s/' % (resource.pk, year, week))
assert 'Sunday' not in resp.text
assert 'Saturday' not in resp.text
# Month starts a Tuesday, but monday is displayed
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
week, year = 21, 2019 # month starts a Saturday
resp = app.get('/manage/resource/%s/%s/week/%s/' % (resource.pk, year, week))
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
saturday = 5
timeperiod_sat = TimePeriod.objects.create(
desk=desk, weekday=saturday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/resource/%s/%s/week/%s/' % (resource.pk, year, week))
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
assert len(resp.pyquery.find('tbody tr:first th.weekday:empty')) == 0
sunday = 6
TimePeriod.objects.create(
desk=desk, weekday=sunday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
)
resp = app.get('/manage/resource/%s/%s/week/%s/' % (resource.pk, year, week))
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
timeperiod_sat.delete()
resp = app.get('/manage/resource/%s/%s/week/%s/' % (resource.pk, year, week))
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
def test_resource_week_view_dst_change(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
TimePeriod.objects.create(
desk=desk, weekday=0, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)
)
login(app)
# book some slots
with freezegun.freeze_time('2019-10-01'):
for start_datetime in [
make_aware(datetime.datetime(2019, 10, 2, 10, 0)),
make_aware(datetime.datetime(2019, 10, 29, 10, 0)),
]:
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=start_datetime,
)
event.resources.add(resource)
Booking.objects.create(event=event)
# check booked slots are similarly aligned
resp = app.get('/manage/resource/%s/2019/week/43/' % resource.pk)
assert resp.text.count('height:50.0%;top:100.0%') == 1
def test_resource_week_view_januaries(app, admin_user):
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
TimePeriod(desk=desk, weekday=2, start_time=datetime.time(9, 0), end_time=datetime.time(12, 0)).save()
for year in range(2020, 2030):
date = datetime.date(year, 1, 1)
with freezegun.freeze_time(date):
login(app)
resp = app.get('/manage/resource/%s/%s/week/01/' % (resource.pk, date.year))
assert resp.text.count('<th></th>') == 1
def test_resource_week_view_event_outside_timeperiod(app, admin_user):
middle_day = now().replace(day=15)
middle_day = middle_day + datetime.timedelta(days=4 - middle_day.weekday())
resource = Resource.objects.create(label='Foo bar')
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
agenda.resources.add(resource)
desk = Desk.objects.create(agenda=agenda, label='Desk')
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
login(app)
# no time period - no events
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert 'No bookings this week.' in resp.text
assert 'div class="booking' not in resp.text
# book some slots
for hour, minute in [(9, 0), (17, 0)]:
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(
day=middle_day.day - middle_day.weekday() + 2, hour=hour, minute=minute
),
)
event.resources.add(resource)
Booking.objects.create(event=event)
# no time period - events are displayed
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert resp.text.count('div class="booking') == 2
# events outside time period
TimePeriod.objects.create(
desk=desk, weekday=2, start_time=datetime.time(10, 0), end_time=datetime.time(16, 0)
)
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert resp.text.count('div class="booking') == 2
assert 'Sunday' not in resp.text
assert 'Saturday' not in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert resp.text.count('div class="booking') == 0
# create an event on saturday
Booking.objects.update(cancellation_datetime=None) # reset
middle_day = now().replace(day=15)
middle_day = middle_day + datetime.timedelta(days=5 - middle_day.weekday())
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(day=middle_day.day, hour=10, minute=0),
)
event.resources.add(resource)
Booking.objects.create(event=event)
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert resp.text.count('div class="booking') == 3
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert resp.text.count('div class="booking') == 0
# and a timeperiod
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=5, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0)
)
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert resp.text.count('div class="booking') == 3
assert 'Sunday' not in resp.text
assert 'Saturday' in resp.text
# create an event on sunday
middle_day = now().replace(day=15)
middle_day = middle_day + datetime.timedelta(days=6 - middle_day.weekday())
event = Event.objects.create(
agenda=agenda,
places=1,
desk=desk,
meeting_type=meetingtype,
start_datetime=localtime(now()).replace(day=middle_day.day, hour=10, minute=0),
)
event.resources.add(resource)
Booking.objects.create(event=event)
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert resp.text.count('div class="booking') == 4
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
# bookings are cancelled
Booking.objects.update(cancellation_datetime=now())
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert resp.text.count('div class="booking') == 0
# and a timeperiod
Booking.objects.update(cancellation_datetime=None) # reset
TimePeriod.objects.create(
desk=desk, weekday=6, start_time=datetime.time(11, 0), end_time=datetime.time(12, 0)
)
resp = app.get(
'/manage/resource/%s/%d/week/%s/' % (resource.pk, middle_day.year, middle_day.strftime('%W'))
)
assert resp.text.count('div class="booking') == 4
assert 'Sunday' in resp.text
assert 'Saturday' in resp.text
def test_week_view_resource_as_manager(app, manager_user):
agenda = Agenda.objects.create(label='Foo Bar', kind='meetings')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
resource = Resource.objects.create(label='Resource 1')
app = login(app, username='manager', password='manager')
today = datetime.date.today()
app.get('/manage/resource/%s/%d/week/%s/' % (resource.pk, today.year, today.strftime('%W')), status=403)
@freezegun.freeze_time('2020-06-15')
def test_resource_month_view(app, admin_user, get_proper_html_str):
resource = Resource.objects.create(label='Foo bar')
@ -261,7 +551,7 @@ def test_resource_month_view(app, admin_user, get_proper_html_str):
today = datetime.date.today()
with CaptureQueriesContext(connection) as ctx:
resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, today.year, today.month))
assert len(ctx.captured_queries) == 8
assert len(ctx.captured_queries) == 9
assert resp.text.count('<div class="booking" style="height:33.0%;') == 2 # booking cells
assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"