agendas: take snapshots (#86634)
gitea/chrono/pipeline/head This commit looks good Details

This commit is contained in:
Lauréline Guérin 2024-02-16 18:10:22 +01:00
parent 9331b06e04
commit 176d23aa4b
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
11 changed files with 386 additions and 18 deletions

View File

@ -46,7 +46,7 @@ from django.utils.functional import cached_property
from django.utils.html import format_html
from django.utils.safestring import SafeString
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from django.utils.translation import ngettext, pgettext
from django.views.generic import (
CreateView,
DayArchiveView,
@ -740,6 +740,11 @@ class ResourceAddView(CreateView):
def get_success_url(self):
return reverse('chrono-manager-resource-view', kwargs={'pk': self.object.id})
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.take_snapshot(request=self.request, comment=pgettext('snapshot', 'created'))
return response
resource_add = ResourceAddView.as_view()
@ -757,6 +762,11 @@ class ResourceEditView(UpdateView):
def get_success_url(self):
return reverse('chrono-manager-resource-view', kwargs={'pk': self.object.id})
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.take_snapshot(request=self.request)
return response
resource_edit = ResourceEditView.as_view()
@ -773,6 +783,10 @@ class ResourceDeleteView(DeleteView):
def get_success_url(self):
return reverse('chrono-manager-resource-list')
def post(self, *args, **kwargs):
self.get_object().take_snapshot(request=self.request, deletion=True)
return super().post(*args, **kwargs)
resource_delete = ResourceDeleteView.as_view()
@ -811,6 +825,11 @@ class CategoryAddView(CreateView):
def get_success_url(self):
return reverse('chrono-manager-category-list')
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.take_snapshot(request=self.request, comment=pgettext('snapshot', 'created'))
return response
category_add = CategoryAddView.as_view()
@ -828,6 +847,11 @@ class CategoryEditView(UpdateView):
def get_success_url(self):
return reverse('chrono-manager-category-list')
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.take_snapshot(request=self.request)
return response
category_edit = CategoryEditView.as_view()
@ -844,6 +868,10 @@ class CategoryDeleteView(DeleteView):
def get_success_url(self):
return reverse('chrono-manager-category-list')
def post(self, *args, **kwargs):
self.get_object().take_snapshot(request=self.request, deletion=True)
return super().post(*args, **kwargs)
category_delete = CategoryDeleteView.as_view()
@ -882,6 +910,11 @@ class EventsTypeAddView(CreateView):
def get_success_url(self):
return reverse('chrono-manager-events-type-list')
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.take_snapshot(request=self.request, comment=pgettext('snapshot', 'created'))
return response
events_type_add = EventsTypeAddView.as_view()
@ -926,6 +959,7 @@ class EventsTypeEditView(UpdateView):
continue
self.object.custom_fields.append(sub_data)
self.object.save()
self.object.take_snapshot(request=self.request)
return response
else:
return self.form_invalid(form)
@ -975,6 +1009,7 @@ class EventsTypeDeleteView(DeleteView):
def post(self, *args, **kwargs):
if self.can_be_deleted():
self.get_object().take_snapshot(request=self.request, deletion=True)
return super().post(*args, **kwargs)
raise PermissionDenied()
@ -1003,6 +1038,7 @@ class AgendaAddView(CreateView):
if self.object.kind == 'events':
desk = Desk.objects.create(agenda=self.object, slug='_exceptions_holder')
desk.import_timeperiod_exceptions_from_settings()
self.object.take_snapshot(request=self.request, comment=pgettext('snapshot', 'created'))
return result
@ -1110,6 +1146,8 @@ class AgendasImportView(FormView):
global_noop = True
for obj_name, obj_results in results.items():
for obj in obj_results['all']:
obj.take_snapshot(request=self.request, comment=_('imported'))
if obj_results['all']:
global_noop = False
count = len(obj_results['created'])
@ -1205,12 +1243,18 @@ class AgendaEditView(ManagedAgendaMixin, UpdateView):
title = _('Edit Agenda')
model = Agenda
form_class = AgendaEditForm
comment = None
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = self.title
return context
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.agenda.take_snapshot(request=self.request, comment=self.comment)
return response
agenda_edit = AgendaEditView.as_view()
@ -1219,6 +1263,7 @@ class AgendaBookingDelaysView(AgendaEditView):
form_class = AgendaBookingDelaysForm
title = _('Configure booking delays')
tab_anchor = 'delays'
comment = _('changed booking delays')
agenda_booking_delays = AgendaBookingDelaysView.as_view()
@ -1228,6 +1273,7 @@ class AgendaRolesView(AgendaEditView):
form_class = AgendaRolesForm
title = _('Configure roles')
tab_anchor = 'permissions'
comment = _('changed roles')
agenda_roles = AgendaRolesView.as_view()
@ -1237,6 +1283,7 @@ class AgendaDisplaySettingsView(AgendaEditView):
form_class = AgendaDisplaySettingsForm
title = _('Configure display options')
tab_anchor = 'display-options'
comment = _('changed display options')
def set_agenda(self, **kwargs):
self.agenda = get_object_or_404(Agenda.objects.exclude(kind='virtual'), pk=kwargs.get('pk'))
@ -1252,6 +1299,7 @@ class AgendaBookingCheckSettingsView(AgendaEditView):
form_class = AgendaBookingCheckSettingsForm
title = _('Configure booking check options')
tab_anchor = 'booking-check-options'
comment = _('changed booking check options')
def set_agenda(self, **kwargs):
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events')
@ -1264,6 +1312,7 @@ class AgendaInvoicingSettingsView(AgendaEditView):
form_class = AgendaInvoicingSettingsForm
title = _('Configure invoicing options')
tab_anchor = 'invoicing-options'
comment = _('changed invoicing options')
def set_agenda(self, **kwargs):
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events', partial_bookings=True)
@ -1297,6 +1346,7 @@ class AgendaDeleteView(DeleteView):
context = self.get_context_data()
if context['cannot_delete']:
raise PermissionDenied()
self.object.take_snapshot(request=self.request, deletion=True)
return super().delete(request, *args, **kwargs)
@ -2453,6 +2503,7 @@ class AgendaDuplicate(FormView):
def form_valid(self, form):
self.new_agenda = self.agenda.duplicate(label=form.cleaned_data['label'])
self.new_agenda.take_snapshot(request=self.request, comment=pgettext('snapshot', 'created'))
return super().form_valid(form)
def get_context_data(self, **kwargs):
@ -2469,6 +2520,11 @@ class AgendaAddEventView(ManagedAgendaMixin, CreateView):
model = Event
form_class = NewEventForm
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.agenda.take_snapshot(request=self.request, comment=_('added event (%s)') % self.object)
return response
agenda_add_event = AgendaAddEventView.as_view()
@ -2485,7 +2541,9 @@ class AgendaEventDuplicateView(ManagedAgendaMixin, UpdateView):
def form_valid(self, form):
messages.success(self.request, _('Event successfully duplicated.'))
return super().form_valid(form)
response = super().form_valid(form)
self.agenda.take_snapshot(request=self.request, comment=_('added event (%s)') % self.object)
return response
event_duplicate = AgendaEventDuplicateView.as_view()
@ -2533,7 +2591,10 @@ class AgendaImportEventsView(ManagedAgendaMixin, FormView):
_('Event "%s" start date has changed. Do not forget to notify the registrants.')
% (event.label or event.slug),
)
return super().form_valid(form)
response = super().form_valid(form)
if form.events:
self.agenda.take_snapshot(request=self.request, comment=_('imported events'))
return response
agenda_import_events = AgendaImportEventsView.as_view()
@ -2613,6 +2674,10 @@ class AgendaDeskManagementToggleView(ManagedAgendaMixin, View):
message = _('Desk global management enabled.')
if message:
messages.info(self.request, message)
if self.agenda.desk_simple_management:
self.agenda.take_snapshot(request=self.request, comment=_('enabled individual management'))
else:
self.agenda.take_snapshot(request=self.request, comment=_('enabled global management'))
return HttpResponseRedirect(reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.pk}))
@ -2638,6 +2703,11 @@ class AgendaNotificationsSettingsView(ManagedAgendaMixin, UpdateView):
self.agenda.event_set.filter(**filter_kwargs).update(**update_kwargs)
return AgendaNotificationsSettings.objects.create(agenda=self.agenda)
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.agenda.take_snapshot(request=self.request, comment=_('changed notification settings'))
return response
agenda_notifications_settings = AgendaNotificationsSettingsView.as_view()
@ -2647,6 +2717,7 @@ class AgendaReminderSettingsView(AgendaEditView):
model = AgendaReminderSettings
form_class = AgendaReminderForm
tab_anchor = 'reminders'
comment = _('changed reminder settings')
def get_object(self):
try:
@ -2958,6 +3029,11 @@ class EventEditView(ManagedAgendaMixin, UpdateView):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id})
return reverse('chrono-manager-event-view', kwargs={'pk': self.agenda.id, 'event_pk': self.object.id})
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.agenda.take_snapshot(request=self.request, comment=_('changed event (%s)') % self.object)
return response
event_edit = EventEditView.as_view()
@ -2981,7 +3057,9 @@ class EventDeleteView(ManagedAgendaMixin, DeleteView):
context = self.get_context_data()
if context['cannot_delete']:
raise PermissionDenied()
return super().delete(request, *args, **kwargs)
response = super().delete(request, *args, **kwargs)
self.agenda.take_snapshot(request=self.request, comment=_('removed event (%s)') % self.object)
return response
def get_success_url(self):
if self.request.GET.get('next') == 'settings' or self.request.POST.get('next') == 'settings':
@ -3175,7 +3253,11 @@ class AgendaAddResourceView(ManagedAgendaMixin, FormView):
def form_valid(self, form):
self.agenda.resources.add(form.cleaned_data['resource'])
return super().form_valid(form)
response = super().form_valid(form)
self.agenda.take_snapshot(
request=self.request, comment=_('added resource (%s)') % form.cleaned_data['resource']
)
return response
agenda_add_resource = AgendaAddResourceView.as_view()
@ -3193,6 +3275,7 @@ class AgendaResourceDeleteView(ManagedAgendaMixin, DeleteView):
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.agenda.resources.remove(self.object)
self.agenda.take_snapshot(request=request, comment=_('removed resource (%s)') % self.object)
return HttpResponseRedirect(self.get_success_url())
@ -3245,6 +3328,15 @@ class UnavailabilityCalendarToggleView(ManagedDeskMixin, DetailView):
message = _('One or several bookings overlap with exceptions.')
messages.warning(self.request, message)
break
self.desk.agenda.take_snapshot(
request=self.request,
comment=_('enabled unavailability calendar (%s)') % unavailability_calendar,
)
else:
self.desk.agenda.take_snapshot(
request=self.request,
comment=_('disabled unavailability calendar (%s)') % unavailability_calendar,
)
return HttpResponseRedirect(
'%s#open:time-periods'
@ -3261,6 +3353,13 @@ class AgendaAddMeetingTypeView(ManagedAgendaMixin, CreateView):
form_class = NewMeetingTypeForm
tab_anchor = 'meeting-types'
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.agenda.take_snapshot(
request=self.request, comment=_('added meeting type (%s)') % self.object
)
return response
agenda_add_meeting_type = AgendaAddMeetingTypeView.as_view()
@ -3274,7 +3373,11 @@ class MeetingTypeEditView(ManagedAgendaSubobjectMixin, UpdateView):
def form_valid(self, form):
try:
with transaction.atomic():
return super().form_valid(form)
response = super().form_valid(form)
self.object.agenda.take_snapshot(
request=self.request, comment=_('changed meeting type (%s)') % self.object
)
return response
except IntegrityError as e:
if 'tstzrange_constraint' in str(e):
form.add_error(
@ -3327,6 +3430,9 @@ class MeetingTypeDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
self.object.deleted = True
self.object.slug += '__deleted__' + str(uuid.uuid4())
self.object.save()
self.object.agenda.take_snapshot(
request=self.request, comment=_('removed meeting type (%s)') % self.object
)
return HttpResponseRedirect(success_url)
@ -3363,6 +3469,7 @@ class AgendaAddTimePeriodView(ManagedDeskMixin, FormView):
process_time_period_add_form(form, desk=desk)
else:
process_time_period_add_form(form, desk=self.desk)
self.desk.agenda.take_snapshot(request=self.request, comment=_('added repeating period'))
return super().form_valid(form)
@ -3379,6 +3486,7 @@ class VirtualAgendaAddTimePeriodView(ManagedAgendaMixin, FormView):
def form_valid(self, form):
process_time_period_add_form(form, agenda=self.agenda)
self.agenda.take_snapshot(request=self.request, comment=_('added excluded period'))
return super().form_valid(form)
@ -3401,6 +3509,23 @@ class TimePeriodEditView(ManagedTimePeriodMixin, UpdateView):
else:
return ['chrono/manager_date_time_period_form.html']
def form_valid(self, form):
response = super().form_valid(form)
if self.object.desk:
if self.object.date:
self.object.desk.agenda.take_snapshot(
request=self.request, comment=_('changed unique period (%s)') % self.object
)
else:
self.object.desk.agenda.take_snapshot(
request=self.request, comment=_('changed repeating period (%s)') % self.object
)
else:
self.object.agenda.take_snapshot(
request=self.request, comment=_('changed excluded period (%s)') % self.object
)
return response
time_period_edit = TimePeriodEditView.as_view()
@ -3410,14 +3535,31 @@ class TimePeriodDeleteView(ManagedTimePeriodMixin, DeleteView):
model = TimePeriod
tab_anchor = 'time-periods'
def take_snapshot(self, time_period):
if self.object.desk:
if time_period.date:
self.time_period.desk.agenda.take_snapshot(
request=self.request, comment=_('removed unique period (%s)') % time_period
)
else:
self.time_period.desk.agenda.take_snapshot(
request=self.request, comment=_('removed repeating period (%s)') % time_period
)
else:
self.object.agenda.take_snapshot(
request=self.request, comment=_('removed excluded period (%s)') % self.object
)
def delete(self, request, *args, **kwargs):
time_period = self.get_object()
response = super().delete(request, *args, **kwargs)
if not time_period.desk:
self.take_snapshot(time_period)
return response
if not time_period.desk.agenda.desk_simple_management:
self.take_snapshot(time_period)
return response
for desk in time_period.desk.agenda.desk_set.exclude(pk=time_period.desk.pk):
@ -3430,6 +3572,7 @@ class TimePeriodDeleteView(ManagedTimePeriodMixin, DeleteView):
if tp is not None:
tp.delete()
self.take_snapshot(time_period)
return response
@ -3451,9 +3594,12 @@ class AgendaAddDateTimePeriodView(ManagedDeskMixin, FormView):
if self.desk.agenda.desk_simple_management:
for desk in self.desk.agenda.desk_set.all():
TimePeriod.objects.create(desk=desk, **create_kwargs)
time_period = TimePeriod.objects.create(desk=desk, **create_kwargs)
else:
TimePeriod.objects.create(desk=self.desk, **create_kwargs)
time_period = TimePeriod.objects.create(desk=self.desk, **create_kwargs)
self.desk.agenda.take_snapshot(
request=self.request, comment=_('added unique period (%s)') % time_period
)
return super().form_valid(form)
@ -3490,6 +3636,11 @@ class AgendaAddDesk(AgendaDeskMixin, ManagedAgendaMixin, CreateView):
def set_agenda(self, **kwargs):
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='meetings')
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.agenda.take_snapshot(request=self.request, comment=_('added desk (%s)') % self.object)
return response
agenda_add_desk = AgendaAddDesk.as_view()
@ -3503,6 +3654,11 @@ class DeskEditView(AgendaDeskMixin, ManagedAgendaSubobjectMixin, UpdateView):
def get_queryset(self):
return super().get_queryset().filter(agenda__kind='meetings')
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.agenda.take_snapshot(request=self.request, comment=_('changed desk (%s)') % self.object)
return response
desk_edit = DeskEditView.as_view()
@ -3534,7 +3690,9 @@ class DeskDeleteView(AgendaDeskMixin, ManagedAgendaSubobjectMixin, DeleteView):
context = self.get_context_data()
if context['cannot_delete']:
raise PermissionDenied()
return super().delete(request, *args, **kwargs)
response = super().delete(request, *args, **kwargs)
self.object.agenda.take_snapshot(request=self.request, comment=_('removed desk (%s)' % self.object))
return response
desk_delete = DeskDeleteView.as_view()
@ -3550,6 +3708,13 @@ class VirtualMemberAddView(ManagedAgendaMixin, CreateView):
kwargs['instance'] = VirtualMember(virtual_agenda=self.agenda)
return kwargs
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.agenda.take_snapshot(
request=self.request, comment=_('added agenda (%s)') % self.object.real_agenda
)
return response
agenda_add_virtual_member = VirtualMemberAddView.as_view()
@ -3601,6 +3766,7 @@ class AgendaAddTimePeriodExceptionView(ManagedDeskMixin, CreateView):
if exception.has_booking_within_time_slot():
messages.warning(self.request, _('One or several bookings exists within this time slot.'))
break
self.desk.agenda.take_snapshot(request=self.request, comment=_('added exception (%s)') % self.object)
return result
@ -3615,6 +3781,14 @@ class TimePeriodExceptionEditView(ManagedTimePeriodExceptionMixin, UpdateView):
def form_valid(self, form):
result = super().form_valid(form)
if not self.desk:
self.unavailability_calendar.take_snapshot(
request=self.request, comment=_('changed unavailability (%s)') % self.object
)
else:
self.desk.agenda.take_snapshot(
request=self.request, comment=_('changed exception (%s)') % self.object
)
messages.info(self.request, _('Exception updated.'))
for exception in form.exceptions:
if exception.has_booking_within_time_slot():
@ -3674,6 +3848,14 @@ class TimePeriodExceptionDeleteView(ManagedTimePeriodExceptionMixin, DeleteView)
def delete(self, request, *args, **kwargs):
exception = self.get_object()
response = super().delete(request, *args, **kwargs)
if not self.desk:
self.unavailability_calendar.take_snapshot(
request=self.request, comment=_('removed unavailability (%s)') % self.object
)
else:
self.desk.agenda.take_snapshot(
request=self.request, comment=_('removed exception (%s)') % self.object
)
if not exception.desk_id:
return response
@ -3754,6 +3936,7 @@ class DeskImportTimePeriodExceptionsView(ManagedAgendaSubobjectMixin, UpdateView
return self.form_invalid(form)
messages.info(self.request, _('Exceptions will be imported in a few minutes.'))
desk.agenda.take_snapshot(request=self.request, comment=_('imported exceptions (%s)') % sources[0])
return super().form_valid(form)
@ -3769,7 +3952,15 @@ class TimePeriodExceptionSourceDeleteView(ManagedTimePeriodExceptionMixin, Delet
source = self.get_object()
response = super().delete(request, *args, **kwargs)
if not source.desk or not source.desk.agenda.desk_simple_management:
if not source.desk:
self.unavailability_calendar.take_snapshot(
request=self.request, comment=_('removed exceptions %s') % source
)
return response
if not source.desk.agenda.desk_simple_management:
self.desk.agenda.take_snapshot(
request=self.request, comment=_('removed exceptions (%s)') % source
)
return response
for desk in source.desk.agenda.desk_set.exclude(pk=source.desk_id):
@ -3781,6 +3972,7 @@ class TimePeriodExceptionSourceDeleteView(ManagedTimePeriodExceptionMixin, Delet
if _source is not None:
_source.delete()
self.desk.agenda.take_snapshot(request=self.request, comment=_('removed exceptions (%s)') % source)
return response
@ -3814,7 +4006,16 @@ class TimePeriodExceptionSourceReplaceView(ManagedTimePeriodExceptionMixin, Upda
return self.form_invalid(form)
messages.info(self.request, _('Exceptions will be synchronized in a few minutes.'))
return super().form_valid(form)
response = super().form_valid(form)
if self.desk:
self.desk.agenda.take_snapshot(
request=self.request, comment=_('imported exceptions (%s)') % self.object
)
else:
self.unavailability_calendar.take_snapshot(
request=self.request, comment=_('imported exceptions (%s)') % self.object
)
return response
time_period_exception_source_replace = TimePeriodExceptionSourceReplaceView.as_view()
@ -4199,6 +4400,14 @@ class TimePeriodExceptionSourceToggleView(ManagedTimePeriodExceptionMixin, Detai
_source.disable()
message = _('Exception source %(source)s has been disabled.')
if was_enabled:
self.desk.agenda.take_snapshot(
request=self.request, comment=_('disabled exceptions (%s)') % source
)
else:
self.desk.agenda.take_snapshot(
request=self.request, comment=_('enabled exceptions (%s)') % source
)
messages.info(self.request, message % {'source': source, 'desk': source.desk})
return HttpResponseRedirect(
'%s#open:time-periods'
@ -4279,6 +4488,11 @@ class UnavailabilityCalendarAddView(CreateView):
def get_success_url(self):
return reverse('chrono-manager-unavailability-calendar-settings', kwargs={'pk': self.object.id})
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.take_snapshot(request=self.request, comment=pgettext('snapshot', 'created'))
return response
unavailability_calendar_add = UnavailabilityCalendarAddView.as_view()
@ -4303,6 +4517,11 @@ class UnavailabilityCalendarEditView(ManagedUnavailabilityCalendarMixin, UpdateV
model = UnavailabilityCalendar
form_class = UnavailabilityCalendarEditForm
def form_valid(self, *args, **kwargs):
response = super().form_valid(*args, **kwargs)
self.object.take_snapshot(request=self.request)
return response
unavailability_calendar_edit = UnavailabilityCalendarEditView.as_view()
@ -4319,12 +4538,14 @@ class UnavailabilityCalendarDeleteView(DeleteView):
def get_success_url(self):
return reverse('chrono-manager-unavailability-calendar-list')
def delete(self, request, *args, **kwargs):
def post(self, *args, **kwargs):
self.get_object().take_snapshot(request=self.request, deletion=True)
try:
return super().delete(request, *args, **kwargs)
return super().post(*args, **kwargs)
except ProtectedError:
messages.warning(
request, _('This calendar cannot be deleted because it is used by shared custody agendas.')
self.request,
_('This calendar cannot be deleted because it is used by shared custody agendas.'),
)
return HttpResponseRedirect(self.get_object().get_absolute_url())
@ -4385,6 +4606,9 @@ class UnavailabilityCalendarAddUnavailabilityView(ManagedUnavailabilityCalendarM
def form_valid(self, form):
result = super().form_valid(form)
self.unavailability_calendar.take_snapshot(
request=self.request, comment=_('added unavailability (%s)') % self.object
)
messages.info(self.request, _('Unavailability added.'))
if self.object.has_booking_within_time_slot():
messages.warning(self.request, _('One or several bookings exists within this time slot.'))
@ -4437,7 +4661,9 @@ class UnavailabilityCalendarImportUnavailabilitiesView(ManagedUnavailabilityCale
return self.form_invalid(form)
messages.info(self.request, _('Exceptions will be imported in a few minutes.'))
return super().form_valid(form)
response = super().form_valid(form)
self.object.take_snapshot(request=self.request, comment=_('imported exceptions (%s)') % source)
return response
unavailability_calendar_import_unavailabilities = UnavailabilityCalendarImportUnavailabilitiesView.as_view()

View File

@ -24,6 +24,7 @@ from chrono.agendas.models import (
UnavailabilityCalendar,
VirtualMember,
)
from chrono.apps.snapshot.models import AgendaSnapshot
from chrono.utils.signature import check_query
from chrono.utils.timezone import localtime, make_aware, now
from tests.utils import login
@ -422,6 +423,7 @@ def test_add_agenda(app, admin_user):
resp = resp.form.submit()
agenda = Agenda.objects.get(label='Foo bar')
assert AgendaSnapshot.objects.count() == 1
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
resp = resp.follow()
assert 'Foo bar' in resp.text
@ -465,6 +467,7 @@ def test_add_agenda_and_set_role(app, admin_user, manager_user):
resp.form['label'] = 'Foo bar'
resp.form['kind'] = 'meetings'
resp = resp.form.submit().follow()
assert AgendaSnapshot.objects.count() == 1
agenda = Agenda.objects.get(label='Foo bar')
assert agenda.desk_set.count() == 1
@ -474,6 +477,7 @@ def test_add_agenda_and_set_role(app, admin_user, manager_user):
resp.form['edit_role'] = manager_user.groups.all()[0].pk
resp = resp.form.submit().follow()
assert 'Edit Role: Managers' in resp.text
assert AgendaSnapshot.objects.count() == 2
# still only one desk
assert agenda.desk_set.count() == 1
@ -527,6 +531,7 @@ def test_options_agenda(app, admin_user):
assert '<h2>Settings' in resp.text
agenda_events.refresh_from_db()
assert agenda_events.anonymize_delay == 365
assert AgendaSnapshot.objects.count() == 1
resp = app.get('/manage/agendas/%s/edit' % agenda_meetings.pk)
assert 'default_view' in resp.context['form'].fields
@ -550,6 +555,7 @@ def test_options_agenda(app, admin_user):
def test_options_events_agenda_events_type(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app)
resp = app.get('/manage/agendas/%s/edit' % agenda.pk)
@ -577,6 +583,7 @@ def test_options_events_agenda_events_type(app, admin_user):
def test_options_events_agenda_delays(settings, app, admin_user):
settings.WORKING_DAY_CALENDAR = None
agenda = Agenda.objects.create(label='Foo bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
assert agenda.minimal_booking_delay == 1
app = login(app)
url = '/manage/agendas/%s/booking-delays' % agenda.pk
@ -593,6 +600,7 @@ def test_options_events_agenda_delays(settings, app, admin_user):
resp = resp.form.submit()
agenda.refresh_from_db()
assert agenda.minimal_booking_delay_in_working_days is True
assert AgendaSnapshot.objects.count() == 1
def test_options_events_agenda_lingo_link(settings, app, admin_user):
@ -624,6 +632,7 @@ def test_options_virtual_agenda_delays(app, admin_user):
def test_options_agenda_booking_display_options(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app)
@ -641,6 +650,7 @@ def test_options_agenda_booking_display_options(app, admin_user):
agenda.refresh_from_db()
assert agenda.booking_user_block_template == '{{ booking.user_name }} Foo Bar'
assert agenda.get_booking_user_block_template() == '{{ booking.user_name }} Foo Bar'
assert AgendaSnapshot.objects.count() == 1
resp = app.get(url)
resp.form['booking_user_block_template'] = ''
@ -686,6 +696,7 @@ def test_options_agenda_booking_display_options(app, admin_user):
def test_options_agenda_booking_check_options(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app)
@ -700,6 +711,7 @@ def test_options_agenda_booking_check_options(app, admin_user):
agenda.refresh_from_db()
assert agenda.booking_check_filters == 'foo,bar,baz'
assert agenda.get_booking_check_filters() == ['foo', 'bar', 'baz']
assert AgendaSnapshot.objects.count() == 1
# check auto checked
assert agenda.mark_event_checked_auto is False
@ -820,6 +832,7 @@ def test_agenda_options_desk_simple_management(available_mock, app, admin_user):
agenda.refresh_from_db()
# was changed
assert agenda.desk_simple_management is not old_value
assert AgendaSnapshot.objects.count() == 2
available_mock.return_value = False
for old_value in [True, False]:
@ -855,6 +868,7 @@ def test_delete_agenda(app, admin_user):
assert resp.location.endswith('/manage/')
resp = resp.follow()
assert 'Foo bar' not in resp.text
assert AgendaSnapshot.objects.count() == 1
def test_delete_busy_agenda(app, admin_user):
@ -2499,6 +2513,7 @@ def test_agenda_view_event(app, manager_user):
def test_agenda_view_edit_event(app, manager_user):
test_agenda_view_event(app, manager_user)
agenda = Agenda.objects.first()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
resp = app.get('/manage/agendas/%s/month/2019/12/01/' % agenda.id, status=200)
resp = resp.click('xyz')
assert 'Options' not in resp.text
@ -2514,10 +2529,12 @@ def test_agenda_view_edit_event(app, manager_user):
resp.form['start_datetime_1'] = agenda.event_set.first().start_datetime.strftime('%H:%M')
resp = resp.form.submit(status=302).follow()
assert event_url == resp.request.url
assert AgendaSnapshot.objects.count() == 1
resp = resp.click('Delete')
resp = resp.form.submit()
assert Event.objects.count() == 0
assert AgendaSnapshot.objects.count() == 2
def test_virtual_agenda_add(app, admin_user):
@ -2531,6 +2548,7 @@ def test_virtual_agenda_add(app, admin_user):
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert agenda.minimal_booking_delay is None
assert agenda.maximal_booking_delay is None
assert AgendaSnapshot.objects.count() == 1
def test_virtual_agenda_day_view(app, admin_user, manager_user):
@ -3001,6 +3019,7 @@ def test_virtual_agenda_settings_include(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert VirtualMember.objects.get(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
assert AgendaSnapshot.objects.count() == 1
resp = resp.follow()
resp = resp.click('Include Agenda')
@ -3028,6 +3047,7 @@ def test_virtual_agenda_settings_add_excluded_period(app, admin_user):
assert tp.start_time.minute == 0
assert tp.end_time.hour == 17
assert tp.end_time.minute == 0
assert AgendaSnapshot.objects.count() == 1
resp = resp.follow()
assert 'Monday / 10 a.m. → 5 p.m.' in resp.text
@ -3054,6 +3074,7 @@ def test_virtual_agenda_settings_edit_excluded_period(app, admin_user):
assert tp.start_time.minute == 0
assert tp.end_time.hour == 18
assert tp.end_time.minute == 0
assert AgendaSnapshot.objects.count() == 1
def test_virtual_agenda_settings_delete_excluded_period(app, admin_user):
@ -3068,6 +3089,7 @@ def test_virtual_agenda_settings_delete_excluded_period(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings#open:time-periods' % agenda.id)
assert TimePeriod.objects.count() == 0
assert AgendaSnapshot.objects.count() == 1
def test_virtual_agenda_settings_include_incompatible_agenda(app, admin_user):
@ -3252,6 +3274,7 @@ def test_duplicate_agenda(app, admin_user):
resp = resp.click('Duplicate')
resp = resp.form.submit()
assert Agenda.objects.count() == 2
assert AgendaSnapshot.objects.count() == 1
new_agenda = Agenda.objects.exclude(pk=agenda.pk).first()
assert resp.location == '/manage/agendas/%s/settings' % new_agenda.pk
@ -3443,6 +3466,7 @@ def test_agenda_notifications(app, admin_user, managers_group):
resp.form['cancelled_event'] = 'use-email-field'
resp = resp.form.submit().follow()
assert 'Notifications are disabled' in resp.text
assert AgendaSnapshot.objects.count() == 1
agenda.view_role = managers_group
agenda.save()
@ -3519,6 +3543,7 @@ def test_manager_reminders(app, admin_user):
resp.form['days_before_email'] = 3
resp.form['email_extra_info'] = 'test'
resp = resp.form.submit().follow()
assert AgendaSnapshot.objects.count() == 1
assert 'Users will be reminded of their booking by email, 3 days in advance.' in resp.text
assert 'reminded of their booking by SMS' not in resp.text
@ -3757,6 +3782,7 @@ 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
assert AgendaSnapshot.objects.count() == 1
@pytest.mark.parametrize(

View File

@ -1,6 +1,7 @@
import pytest
from chrono.agendas.models import Agenda, Category
from chrono.apps.snapshot.models import CategorySnapshot
from tests.utils import login
pytestmark = pytest.mark.django_db
@ -28,6 +29,7 @@ def test_add_category(app, admin_user):
assert resp.location.endswith('/manage/categories/')
assert category.label == 'Foo bar'
assert category.slug == 'foo-bar'
assert CategorySnapshot.objects.count() == 1
def test_add_category_as_manager(app, manager_user):
@ -51,6 +53,7 @@ def test_edit_category(app, admin_user):
category.refresh_from_db()
assert category.label == 'Foo bar baz'
assert category.slug == 'baz'
assert CategorySnapshot.objects.count() == 1
def test_edit_category_as_manager(app, manager_user):
@ -71,6 +74,7 @@ def test_delete_category(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/categories/')
assert Category.objects.exists() is False
assert CategorySnapshot.objects.count() == 1
def test_delete_category_as_manager(app, manager_user):

View File

@ -12,6 +12,7 @@ from django.test.utils import CaptureQueriesContext
from webtest import Upload
from chrono.agendas.models import Agenda, Booking, Desk, Event, EventsType, Lease, Subscription
from chrono.apps.snapshot.models import AgendaSnapshot
from chrono.utils.lingo import CheckType
from chrono.utils.timezone import localtime, make_aware, now
from tests.utils import login
@ -36,6 +37,7 @@ def test_add_event(app, admin_user):
resp.form['places'] = 10
resp = resp.form.submit()
resp = resp.follow()
assert AgendaSnapshot.objects.count() == 1
event = Event.objects.get(places=10)
assert event.publication_datetime is None
assert "This agenda doesn't have any event yet." not in resp.text
@ -81,6 +83,7 @@ def test_add_recurring_event(app, admin_user):
resp.form['frequency'] = 'unique' # not a recurring event
resp.form['recurrence_days'] = [2]
resp.form.submit().follow()
assert AgendaSnapshot.objects.count() == 1
event = Event.objects.get()
assert event.recurrence_days is None
@ -89,6 +92,7 @@ def test_add_recurring_event(app, admin_user):
# add recurring event
resp.form['frequency'] = 'recurring'
resp.form.submit().follow()
assert AgendaSnapshot.objects.count() == 2
event = Event.objects.get(primary_event__isnull=True)
assert event.recurrence_days == [2]
@ -173,6 +177,7 @@ def test_add_event_third_millennium(app, admin_user):
def test_edit_event(settings, app, admin_user):
settings.LANGUAGE_CODE = 'fr-fr' # check date initial value format
agenda = Agenda.objects.create(label='Foo bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Foo',
start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)),
@ -214,6 +219,7 @@ def test_edit_event(settings, app, admin_user):
resp = resp.form.submit()
settings.LANGUAGE_CODE = 'en'
resp = resp.follow()
assert AgendaSnapshot.objects.count() == 1
assert '/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id) in resp.text
assert 'Feb. 16, 2016, 5 p.m.' in resp.text
event.refresh_from_db()
@ -295,6 +301,7 @@ def test_edit_event_with_custom_fields(app, admin_user):
],
)
agenda = Agenda.objects.create(label='Foo', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
app = login(app)
@ -376,6 +383,7 @@ def test_edit_recurring_event(settings, app, admin_user, freezer):
resp.form['frequency'] = 'recurring'
resp.form['recurrence_days'] = [localtime().isoweekday()]
resp = resp.form.submit()
assert AgendaSnapshot.objects.count() == 1
# no end date, events are created for the year to come
assert Event.objects.count() == 54
@ -482,6 +490,7 @@ def test_edit_recurring_event(settings, app, admin_user, freezer):
def test_edit_recurring_event_with_end_date(settings, app, admin_user, freezer):
freezer.move_to('2021-01-12 12:10')
agenda = Agenda.objects.create(label='Foo bar', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
start_datetime=now(), places=10, recurrence_days=list(range(1, 8)), agenda=agenda
)
@ -632,6 +641,7 @@ def test_delete_event(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
assert Event.objects.count() == 0
assert AgendaSnapshot.objects.count() == 1
def test_delete_busy_event(app, admin_user):
@ -729,6 +739,7 @@ def test_delete_event_as_manager(app, manager_user):
def test_export_events(app, admin_user):
agenda = Agenda.objects.create(label='Foo bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app)
resp = app.get('/manage/agendas/%s/export-events' % agenda.id)
@ -794,6 +805,7 @@ def test_import_events(app, admin_user):
resp = resp.form.submit(status=302)
assert Event.objects.count() == 1
Event.objects.all().delete()
assert AgendaSnapshot.objects.count() == 1
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
resp.form['events_csv_file'] = Upload('t.csv', b'xx', 'text/csv')
@ -1014,7 +1026,7 @@ def test_import_events(app, admin_user):
)
with CaptureQueriesContext(connection) as ctx:
resp = resp.form.submit(status=302)
assert len(ctx.captured_queries) == 22
assert len(ctx.captured_queries) == 32
assert Event.objects.count() == 5
assert set(Event.objects.values_list('slug', flat=True)) == {
'labelb',
@ -1033,8 +1045,8 @@ def test_import_events(app, admin_user):
def test_import_event_nested_quotes(app, admin_user):
agenda = Agenda(label='Foo bar')
agenda.save()
agenda = Agenda.objects.create(label='Foo bar')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app)
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200)
@ -3337,6 +3349,7 @@ def test_duplicate_event(app, admin_user):
resp = resp.form.submit().follow()
assert Event.objects.count() == 2
assert AgendaSnapshot.objects.count() == 1
duplicate = Event.objects.latest('pk')
@ -3375,6 +3388,7 @@ def test_duplicate_event_creates_recurrences(app, admin_user):
resp = resp.form.submit().follow()
duplicate = Event.objects.filter(primary_event__isnull=True).latest('pk')
assert AgendaSnapshot.objects.count() == 1
assert duplicate != recurring_event
assert duplicate.recurrences.count() == 6

View File

@ -1,6 +1,7 @@
import pytest
from chrono.agendas.models import Agenda, EventsType
from chrono.apps.snapshot.models import EventsTypeSnapshot
from tests.utils import login
pytestmark = pytest.mark.django_db
@ -29,6 +30,7 @@ def test_add_events_type(app, admin_user):
assert events_type.label == 'Foo bar'
assert events_type.slug == 'foo-bar'
assert events_type.custom_fields == []
assert EventsTypeSnapshot.objects.count() == 1
def test_add_events_type_as_manager(app, manager_user):
@ -58,6 +60,7 @@ def test_edit_events_type(app, admin_user):
assert events_type.label == 'Foo bar baz'
assert events_type.slug == 'baz2'
assert events_type.custom_fields == []
assert EventsTypeSnapshot.objects.count() == 1
def test_edit_events_type_custom_fields(app, admin_user):
@ -160,6 +163,7 @@ def test_delete_events_type(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/events-types/')
assert EventsType.objects.exists() is False
assert EventsTypeSnapshot.objects.count() == 1
def test_delete_referenced_events_type(app):

View File

@ -23,6 +23,7 @@ from chrono.agendas.models import (
TimePeriodExceptionSource,
UnavailabilityCalendar,
)
from chrono.apps.snapshot.models import AgendaSnapshot
from chrono.manager.forms import TimePeriodExceptionForm
from chrono.utils.timezone import localtime, make_aware, now
from tests.utils import login
@ -42,6 +43,7 @@ def test_add_agenda_exceptions_from_settings(app, admin_user):
resp.form['label'] = 'Foo bar'
resp.form['kind'] = 'meetings'
resp = resp.form.submit().follow()
assert AgendaSnapshot.objects.count() == 1
agenda = Agenda.objects.get(label='Foo bar')
assert agenda.desk_set.count() == 1
@ -58,6 +60,7 @@ def test_add_agenda_exceptions_from_settings(app, admin_user):
resp = app.get('/manage/agendas/%s/add-desk' % agenda.pk)
resp.form['label'] = 'Desk A'
resp = resp.form.submit().follow()
assert AgendaSnapshot.objects.count() == 2
desk = Desk.objects.get(slug='desk-a')
assert desk.timeperiodexception_set.exists()
@ -93,6 +96,7 @@ def test_meetings_agenda_add_time_period_exception(app, admin_user):
assert localtime(time_period_exception.end_datetime).strftime(dt_format) == tomorrow.replace(
hour=16
).strftime(dt_format)
assert AgendaSnapshot.objects.count() == 1
# add an exception beyond 2 weeks and make sure it isn't listed
resp = resp.click('Add a time period exception', index=0)
future = tomorrow + datetime.timedelta(days=15)
@ -184,6 +188,7 @@ def test_meetings_agenda_add_time_period_exception_all_desks(app, admin_user):
assert TimePeriodException.objects.count() == 2
assert 'Exceptions added.' in resp.text
assert 'One or several bookings exists within this time slot.' in resp.text
assert AgendaSnapshot.objects.count() == 1
exception = TimePeriodException.objects.first()
resp = app.get('/manage/time-period-exceptions/%s/edit' % exception.pk)
@ -267,6 +272,7 @@ def test_meetings_agenda_add_time_period_exception_desk_simple_management(app, a
assert 'One or several bookings exists within this time slot.' in resp.text
assert TimePeriodException.objects.count() == 2
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
def test_meetings_agenda_edit_time_period_exception(app, admin_user):
@ -289,6 +295,7 @@ def test_meetings_agenda_edit_time_period_exception(app, admin_user):
assert exception.start_datetime == make_aware(datetime.datetime(2018, 12, 16, 8, 0))
exception2.refresh_from_db()
assert exception2.start_datetime == make_aware(datetime.datetime(2018, 12, 16, 5, 0))
assert AgendaSnapshot.objects.count() == 1
def test_meetings_agenda_edit_time_period_exception_overlaps(app, admin_user):
@ -370,6 +377,7 @@ def test_meetings_agenda_edit_time_period_exception_desk_simple_management(app,
assert exception2.start_datetime == make_aware(datetime.datetime(2017, 5, 22, 8, 0))
assert exception2.end_datetime == make_aware(datetime.datetime(2017, 5, 22, 17, 30))
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
# should not happen: corresponding exception does not exist
exception2.delete()
@ -441,6 +449,7 @@ def test_meetings_agenda_delete_time_period_exception(app, admin_user):
resp = resp.form.submit().follow()
assert TimePeriodException.objects.count() == 1
assert resp.request.url.endswith('/manage/agendas/%d/settings' % agenda.pk)
assert AgendaSnapshot.objects.count() == 1
# stay on exception list
time_period_exception = TimePeriodException.objects.create(
@ -475,6 +484,7 @@ def test_meetings_agenda_delete_time_period_exception_desk_simple_management(app
resp.form.submit()
assert TimePeriodException.objects.count() == 0
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
# should not happen: corresponding exception does not exist
exception = TimePeriodException.objects.create(
@ -639,6 +649,7 @@ END:VCALENDAR"""
assert source.ics_url is None
resp = resp.follow()
assert 'Exceptions will be imported in a few minutes.' in resp.text
assert AgendaSnapshot.objects.count() == 1
@pytest.mark.freeze_time('2017-12-01')
@ -698,6 +709,7 @@ END:VCALENDAR"""
assert source.ics_filename is None
assert source.ics_file.name == ''
assert source.ics_url == 'http://example.com/foo.ics'
assert AgendaSnapshot.objects.count() == 1
@mock.patch('chrono.agendas.models.requests.get')
@ -866,6 +878,7 @@ END:VCALENDAR"""
desk = Desk.objects.create(agenda=agenda, label='New Desk')
desk.duplicate()
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
def test_agenda_import_time_period_exception_file_desk_all_desks(app, admin_user):
@ -901,6 +914,7 @@ END:VCALENDAR"""
desk = Desk.objects.create(agenda=agenda, label='New Desk')
desk.duplicate()
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
@mock.patch('chrono.agendas.models.requests.get')
@ -928,6 +942,7 @@ END:VCALENDAR"""
resp = resp.form.submit(status=302)
assert TimePeriodException.objects.count() == 2
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
def test_agenda_import_time_period_exception_file_desk_simple_management(app, admin_user):
@ -952,6 +967,7 @@ END:VCALENDAR"""
resp = resp.form.submit(status=302)
assert TimePeriodException.objects.count() == 2
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
def test_meetings_agenda_delete_time_period_exception_source(app, admin_user):
@ -982,6 +998,7 @@ def test_meetings_agenda_delete_time_period_exception_source(app, admin_user):
assert TimePeriodExceptionSource.objects.count() == 3
assert source1.timeperiodexception_set.count() == 1
assert TimePeriodExceptionSource.objects.filter(pk=source2.pk).exists() is False
assert AgendaSnapshot.objects.count() == 1
def test_meetings_agenda_delete_time_period_exception_source_desk_simple_management(app, admin_user):
@ -997,6 +1014,7 @@ def test_meetings_agenda_delete_time_period_exception_source_desk_simple_managem
resp.form.submit()
assert TimePeriodExceptionSource.objects.count() == 0
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
# should not happen: corresponding source does not exist
source = TimePeriodExceptionSource.objects.create(desk=desk, ics_url='https://example.com/test.ics')
@ -1031,6 +1049,7 @@ END:VCALENDAR"""
assert source.timeperiodexception_set.count() == 2
exceptions = list(source.timeperiodexception_set.order_by('pk'))
old_ics_file_path = source.ics_file.path
assert AgendaSnapshot.objects.count() == 1
desk2 = desk.duplicate()
source2 = desk2.timeperiodexceptionsource_set.get()
@ -1057,6 +1076,7 @@ END:VCALENDAR"""
new_exceptions2 = list(source2.timeperiodexception_set.order_by('pk'))
assert exceptions2[0].pk == new_exceptions2[0].pk
assert exceptions2[1].pk == new_exceptions2[1].pk
assert AgendaSnapshot.objects.count() == 2
def test_meetings_agenda_replace_time_period_exception_source_desk_simple_management(app, admin_user):
@ -1102,6 +1122,7 @@ END:VCALENDAR"""
assert source2.ics_filename == 'exceptions-bis.ics'
assert os.path.exists(old_ics_file_path2) is False
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
# should not happen: corresponding source does not exist
source2.delete()
@ -1265,6 +1286,7 @@ def test_meetings_agenda_time_period_exception_source_from_settings_toggle_desk_
assert not desk.timeperiodexception_set.exists()
assert not desk2.timeperiodexception_set.exists()
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
app.get('/manage/time-period-exceptions-source/%s/toggle' % source.pk)
source.refresh_from_db()

View File

@ -12,10 +12,19 @@ from chrono.agendas.models import (
Category,
Desk,
Event,
EventsType,
MeetingType,
Resource,
SharedCustodySettings,
UnavailabilityCalendar,
)
from chrono.apps.snapshot.models import (
AgendaSnapshot,
CategorySnapshot,
EventsTypeSnapshot,
ResourceSnapshot,
UnavailabilityCalendarSnapshot,
)
from chrono.utils.timezone import now
from tests.utils import login
@ -68,6 +77,29 @@ def test_export_site(app, admin_user):
assert 'resources' not in site_json
assert 'categories' not in site_json
Category.objects.create(label='cat')
Resource.objects.create(label='resource')
EventsType.objects.create(label='events-type')
resp = app.get('/manage/agendas/export/')
resp = resp.form.submit()
site_text = resp.text
site_json = json.loads(site_text)
assert len(site_json['agendas']) == 1
assert len(site_json['unavailability_calendars']) == 1
assert len(site_json['events_types']) == 1
assert len(site_json['resources']) == 1
assert len(site_json['categories']) == 1
resp = app.get('/manage/agendas/import/')
resp.form['agendas_json'] = Upload('export.json', site_text.encode('utf-8'), 'application/json')
resp = resp.form.submit().follow()
assert UnavailabilityCalendar.objects.count() == 1
assert AgendaSnapshot.objects.count() == 1
assert CategorySnapshot.objects.count() == 1
assert EventsTypeSnapshot.objects.count() == 1
assert ResourceSnapshot.objects.count() == 1
assert UnavailabilityCalendarSnapshot.objects.count() == 1
def test_import_agenda_as_manager(app, manager_user):
# open /manage/ access to manager_user, and check agenda import is not

View File

@ -16,6 +16,7 @@ from chrono.agendas.models import (
TimePeriodExceptionSource,
UnavailabilityCalendar,
)
from chrono.apps.snapshot.models import AgendaSnapshot
from chrono.utils.timezone import localtime, now
from tests.utils import login
@ -133,6 +134,7 @@ def test_meetings_agenda_add_meeting_type(app, admin_user):
assert meeting_type.deleted is False
resp = resp.follow()
assert 'Blah' in resp.text
assert AgendaSnapshot.objects.count() == 1
def test_meetings_agenda_edit_meeting_type(app, admin_user):
@ -154,6 +156,7 @@ def test_meetings_agenda_edit_meeting_type(app, admin_user):
assert meeting_type.slug == other_meeting_type.slug
assert meeting_type.duration == 120
assert meeting_type.deleted is False
assert AgendaSnapshot.objects.count() == 1
# check slug edition
resp = app.get('/manage/meetingtypes/%s/edit' % meeting_type.pk)
@ -211,6 +214,7 @@ def test_meetings_agenda_delete_meeting_type(app, admin_user):
meeting_type.refresh_from_db()
assert meeting_type.deleted is True
assert '__deleted__' in meeting_type.slug
assert AgendaSnapshot.objects.count() == 1
# meeting type not showing up anymore
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
@ -233,11 +237,13 @@ def test_meetings_agenda_add_desk(app, admin_user):
agenda = Agenda.objects.get(slug='foo-bar')
agenda.desk_simple_management = False
agenda.save()
assert AgendaSnapshot.objects.count() == 1
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('New Desk')
resp.form['label'] = 'Desk A'
resp = resp.form.submit().follow()
assert AgendaSnapshot.objects.count() == 2
assert Desk.objects.count() == 2
desk = Desk.objects.latest('pk')
TimePeriod.objects.create(
@ -291,6 +297,7 @@ def test_meetings_agenda_add_desk_from_another(app, admin_user):
assert (
new_desk.timeperiodexceptionsource_set.count() == 0
) # holidays not automatically added via duplication
assert AgendaSnapshot.objects.count() == 1
@override_settings(
@ -319,6 +326,7 @@ def test_meetings_agenda_add_desk_simple_management(app, admin_user):
assert (
new_desk.timeperiodexceptionsource_set.count() == 0
) # holidays not automatically added via duplication
assert AgendaSnapshot.objects.count() == 1
# ok if no desks (should not happen)
Desk.objects.all().delete()
@ -350,6 +358,7 @@ def test_meetings_agenda_edit_desk(app, admin_user):
desk.refresh_from_db()
assert desk.label == 'Desk C'
assert desk.slug == other_desk.slug
assert AgendaSnapshot.objects.count() == 1
# check slug edition
resp = app.get('/manage/desks/%s/edit' % desk.pk)
@ -382,6 +391,7 @@ def test_meetings_agenda_delete_desk(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings#open:time-periods' % agenda.pk)
assert Desk.objects.count() == 1
assert AgendaSnapshot.objects.count() == 1
# only one desk
app.get('/manage/desks/%s/delete' % desk_b.pk, status=404)

View File

@ -6,6 +6,7 @@ from django.db import connection
from django.test.utils import CaptureQueriesContext
from chrono.agendas.models import Agenda, Booking, Desk, Event, MeetingType, Resource, TimePeriod
from chrono.apps.snapshot.models import AgendaSnapshot, ResourceSnapshot
from chrono.utils.timezone import localtime, make_aware, now
from tests.utils import login
@ -34,6 +35,7 @@ def test_add_resource(app, admin_user):
assert resp.location.endswith('/manage/resource/%s/' % resource.pk)
assert resource.label == 'Foo bar'
assert resource.slug == 'foo-bar'
assert ResourceSnapshot.objects.count() == 1
def test_add_resource_as_manager(app, manager_user):
@ -881,6 +883,7 @@ def test_edit_resource(app, admin_user):
resource.refresh_from_db()
assert resource.label == 'Foo bar baz'
assert resource.slug == 'baz'
assert ResourceSnapshot.objects.count() == 1
def test_edit_resource_as_manager(app, manager_user):
@ -901,6 +904,7 @@ def test_delete_resource(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/resources/')
assert Resource.objects.exists() is False
assert ResourceSnapshot.objects.count() == 1
def test_delete_resource_as_manager(app, manager_user):
@ -941,6 +945,7 @@ def test_meetings_agenda_resources(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings#open:resources' % agenda.pk)
assert list(agenda.resources.all()) == [resource]
assert AgendaSnapshot.objects.count() == 1
resp = resp.follow()
assert '/manage/resource/%s/' % resource.pk in resp.text
assert '/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk) in resp.text
@ -951,6 +956,7 @@ def test_meetings_agenda_resources(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings#open:resources' % agenda.pk)
assert list(agenda.resources.all()) == []
assert AgendaSnapshot.objects.count() == 2
resp = resp.follow()
assert '/manage/resource/%s/' % resource.pk not in resp.text
assert '/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk) not in resp.text

View File

@ -3,6 +3,7 @@ import datetime
import pytest
from chrono.agendas.models import Agenda, Desk, MeetingType, TimePeriod
from chrono.apps.snapshot.models import AgendaSnapshot
from tests.utils import login
pytestmark = pytest.mark.django_db
@ -28,6 +29,7 @@ def test_meetings_agenda_add_time_period(app, admin_user):
assert TimePeriod.objects.get(desk=desk).weekday_indexes is None
assert desk2.timeperiod_set.exists() is False
resp = resp.follow()
assert AgendaSnapshot.objects.count() == 1
# add a second time period
resp = resp.click('Add repeating periods', index=0)
@ -72,6 +74,7 @@ def test_meetings_agenda_add_time_period_desk_simple_management(app, admin_user)
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '13:00'
resp.form.submit()
assert AgendaSnapshot.objects.count() == 1
assert TimePeriod.objects.filter(desk=desk).count() == 1
assert TimePeriod.objects.filter(desk=desk2).count() == 1
@ -136,6 +139,7 @@ def test_meetings_agenda_edit_time_period(app, admin_user):
assert time_period.start_time.hour == 10
time_period2.refresh_from_db()
assert time_period2.start_time.hour == 9
assert AgendaSnapshot.objects.count() == 1
resp = resp.click('Monday / 10 a.m. → noon')
resp.form['repeat'] = 'custom'
@ -182,6 +186,7 @@ def test_meetings_agenda_edit_time_period_desk_simple_management(app, admin_user
assert time_period2.start_time.hour == 10
assert time_period2.end_time.hour == 11
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
# should not happen: corresponding time period does not exist
time_period2.delete()
@ -213,6 +218,7 @@ def test_meetings_agenda_delete_time_period(app, admin_user):
resp = resp.form.submit()
assert resp.location.endswith('/manage/agendas/%s/settings#open:time-periods' % agenda.id)
assert TimePeriod.objects.count() == 1
assert AgendaSnapshot.objects.count() == 1
def test_meetings_agenda_delete_time_period_desk_simple_management(app, admin_user):
@ -230,6 +236,7 @@ def test_meetings_agenda_delete_time_period_desk_simple_management(app, admin_us
resp.form.submit()
assert TimePeriod.objects.count() == 0
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 1
# should not happen: corresponding time period does not exist
time_period = TimePeriod.objects.create(
@ -262,6 +269,7 @@ def test_meetings_agenda_date_time_period(app, admin_user):
assert TimePeriod.objects.get(desk=desk).end_time.minute == 0
assert desk2.timeperiod_set.exists() is False
resp = resp.follow()
assert AgendaSnapshot.objects.count() == 1
# invert start and end
resp = resp.click('Add a unique period', index=0)
@ -301,6 +309,7 @@ def test_meetings_agenda_date_time_period_desk_simple_management(app, admin_user
assert TimePeriod.objects.filter(desk=desk).count() == 1
assert TimePeriod.objects.filter(desk=desk2).count() == 1
assert AgendaSnapshot.objects.count() == 1
# edit
resp = resp.click('Monday 24')

View File

@ -15,6 +15,7 @@ from chrono.agendas.models import (
TimePeriodExceptionSource,
UnavailabilityCalendar,
)
from chrono.apps.snapshot.models import AgendaSnapshot, UnavailabilityCalendarSnapshot
from chrono.utils.timezone import localtime, make_aware, now
from tests.utils import login
@ -51,6 +52,7 @@ def test_add_unavailability_calendar(app, admin_user):
resp = app.get('/manage/unavailability-calendars/')
assert 'Foo bar' in resp.text
assert 'foo-bar' in resp.text
assert UnavailabilityCalendarSnapshot.objects.count() == 1
def test_used_unavailability_calendar(app, admin_user):
@ -83,6 +85,7 @@ def test_edit_unavailability_calendar(app, admin_user):
assert 'Bar' in resp.text
unavailability_calendar.refresh_from_db()
assert unavailability_calendar.label == 'Bar'
assert UnavailabilityCalendarSnapshot.objects.count() == 1
def test_delete_unavailability_calendar(app, admin_user):
@ -95,6 +98,7 @@ def test_delete_unavailability_calendar(app, admin_user):
resp = resp.follow()
assert 'Foo' not in resp.text
assert UnavailabilityCalendar.objects.count() == 0
assert UnavailabilityCalendarSnapshot.objects.count() == 1
def test_unavailability_calendar_add_time_period_exceptions(app, admin_user):
@ -117,6 +121,7 @@ def test_unavailability_calendar_add_time_period_exceptions(app, admin_user):
assert 'Unavailability added.' in resp.text
assert 'One or several bookings exists within this time slot.' not in resp.text
assert TimePeriodException.objects.count() == 1
assert UnavailabilityCalendarSnapshot.objects.count() == 1
time_period_exception = TimePeriodException.objects.first()
assert time_period_exception.unavailability_calendar == unavailability_calendar
@ -170,6 +175,7 @@ def test_unavailability_calendar_edit_time_period_exceptions(app, admin_user):
assert 'Exception foo' in resp.text
time_period_exception.refresh_from_db()
assert time_period_exception.label == 'Exception foo'
assert UnavailabilityCalendarSnapshot.objects.count() == 1
# with an error
resp = app.get('/manage/time-period-exceptions/%s/edit' % time_period_exception.pk)
@ -195,6 +201,7 @@ def test_unavailability_calendar_delete_time_period_exceptions(app, admin_user):
resp = resp.form.submit().follow()
assert 'Exception foo' not in resp.text
assert unavailability_calendar.timeperiodexception_set.count() == 0
assert UnavailabilityCalendarSnapshot.objects.count() == 1
def test_unavailability_calendar_import_time_period_exception_from_ics(app, admin_user):
@ -234,6 +241,7 @@ END:VCALENDAR"""
assert source.ics_url is None
resp = resp.follow()
assert 'Exceptions will be imported in a few minutes.' in resp.text
assert UnavailabilityCalendarSnapshot.objects.count() == 1
@mock.patch('chrono.agendas.models.requests.get')
@ -295,6 +303,7 @@ def test_unavailability_calendar_delete_time_period_exception_source(app, admin_
assert TimePeriodExceptionSource.objects.count() == 1
assert source1.timeperiodexception_set.count() == 1
assert TimePeriodExceptionSource.objects.filter(pk=source2.pk).exists() is False
assert UnavailabilityCalendarSnapshot.objects.count() == 1
def test_unavailability_calendar_replace_time_period_exception_source(app, admin_user, freezer):
@ -321,6 +330,7 @@ END:VCALENDAR"""
assert source.timeperiodexception_set.count() == 2
exceptions = list(source.timeperiodexception_set.order_by('pk'))
old_ics_file_path = source.ics_file.path
assert UnavailabilityCalendarSnapshot.objects.count() == 1
# replace the source
resp = app.get('/manage/time-period-exceptions-source/%d/replace' % source.pk)
@ -335,6 +345,7 @@ END:VCALENDAR"""
new_exceptions = list(source.timeperiodexception_set.order_by('pk'))
assert exceptions[0].pk != new_exceptions[0].pk
assert exceptions[1].pk != new_exceptions[1].pk
assert UnavailabilityCalendarSnapshot.objects.count() == 2
@mock.patch('chrono.agendas.models.requests.get')
@ -402,6 +413,7 @@ def test_unavailability_calendar_in_desk(app, admin_user):
resp = app.get(settings_url)
assert 'what' in resp.text
assert 'One or several bookings overlap with exceptions.' not in resp.text
assert AgendaSnapshot.objects.count() == 1
resp = app.get(exceptions_url)
assert 'calendar' in resp.text
@ -418,6 +430,7 @@ def test_unavailability_calendar_in_desk(app, admin_user):
assert 'calendar' in resp.text
assert 'enable' in resp.text
assert not desk.unavailability_calendars.exists()
assert AgendaSnapshot.objects.count() == 2
# info message if some exceptions overlaps with a booking
meeting_type = MeetingType.objects.create(label='Meeting Type', agenda=agenda)
@ -477,11 +490,13 @@ def test_unavailability_calendar_in_desk_desk_simple_management(app, admin_user)
assert agenda.is_available_for_simple_management() is True
resp = resp.follow()
assert 'One or several bookings overlap with exceptions.' in resp.text
assert AgendaSnapshot.objects.count() == 1
app.get('/manage/desk/%s/unavailability-calendar/%s/toggle/' % (desk.pk, unavailability_calendar.pk))
assert not desk.unavailability_calendars.exists()
assert not desk2.unavailability_calendars.exists()
assert agenda.is_available_for_simple_management() is True
assert AgendaSnapshot.objects.count() == 2
# should not happen: unavailability_calendar is not in the correct state
desk2.unavailability_calendars.add(unavailability_calendar)