chrono/chrono/manager/views.py

800 lines
30 KiB
Python

# chrono - agendas system
# Copyright (C) 2016 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import json
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse, reverse_lazy
from django.db.models import Q
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.utils.dates import MONTHS
from django.utils.timezone import now, make_aware
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext
from django.utils.encoding import force_text
from django.views.generic import (DetailView, CreateView, UpdateView,
ListView, DeleteView, FormView, TemplateView, DayArchiveView,
MonthArchiveView)
from chrono.agendas.models import (Agenda, Event, MeetingType, TimePeriod,
Booking, Desk, TimePeriodException, ICSError)
from .forms import (AgendaAddForm, AgendaEditForm, EventForm, NewMeetingTypeForm, MeetingTypeForm,
TimePeriodForm, ImportEventsForm, NewDeskForm, DeskForm, TimePeriodExceptionForm,
ExceptionsImportForm, AgendasImportForm, TimePeriodAddForm)
from .utils import import_site
class HomepageView(ListView):
template_name = 'chrono/manager_home.html'
model = Agenda
def get_queryset(self):
queryset = super(HomepageView, self).get_queryset()
if not self.request.user.is_staff:
group_ids = [x.id for x in self.request.user.groups.all()]
queryset = queryset.filter(Q(view_role_id__in=group_ids) | Q(edit_role_id__in=group_ids))
return queryset
homepage = HomepageView.as_view()
class AgendaAddView(CreateView):
template_name = 'chrono/manager_agenda_form.html'
model = Agenda
form_class = AgendaAddForm
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
raise PermissionDenied()
return super(AgendaAddView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
model_form = super(AgendaAddView, self).form_valid(form)
if self.object.kind == 'meetings':
default_desk = Desk(agenda=self.object, label=_('Desk 1'))
default_desk.save()
return model_form
def get_success_url(self):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.object.id})
agenda_add = AgendaAddView.as_view()
class AgendasImportView(FormView):
form_class = AgendasImportForm
template_name = 'chrono/agendas_import.html'
success_url = reverse_lazy('chrono-manager-homepage')
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
raise PermissionDenied()
return super(AgendasImportView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
try:
agendas_json = json.loads(force_text(self.request.FILES['agendas_json'].read()))
except ValueError:
form.add_error('agendas_json', _('File is not in the expected JSON format.'))
return self.form_invalid(form)
results = import_site(agendas_json, overwrite=True)
if results.get('created') == 0 and results.get('updated') == 0:
messages.info(self.request, _('No agendas were found.'))
else:
if results.get('created') == 0:
message1 = _('No agenda created.')
else:
message1 = ungettext('An agenda has been created.',
'%(count)d agendas have been created.', results['created'])
if results.get('updated') == 0:
message2 = _('No agenda updated.')
else:
message2 = ungettext('An agenda has been updated.',
'%(count)d agendas have been updated.', results['updated'])
messages.info(self.request, u'%s %s' % (message1, message2))
return super(AgendasImportView, self).form_valid(form)
agendas_import = AgendasImportView.as_view()
class AgendaEditView(UpdateView):
template_name = 'chrono/manager_agenda_form.html'
model = Agenda
form_class = AgendaEditForm
def get_object(self, queryset=None):
obj = super(AgendaEditView, self).get_object(queryset=queryset)
if not obj.can_be_managed(self.request.user):
raise PermissionDenied()
return obj
def get_success_url(self):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.object.id})
agenda_edit = AgendaEditView.as_view()
class AgendaDeleteView(DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = Agenda
success_url = reverse_lazy('chrono-manager-homepage')
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
raise PermissionDenied()
return super(AgendaDeleteView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(AgendaDeleteView, self).get_context_data(**kwargs)
context['cannot_delete'] = Booking.objects.filter(
event__agenda=self.get_object(),
event__start_datetime__gt=now(),
cancellation_datetime__isnull=True).exists()
return context
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data()
if context['cannot_delete']:
raise PermissionDenied()
return super(AgendaDeleteView, self).delete(request, *args, **kwargs)
agenda_delete = AgendaDeleteView.as_view()
class AgendaView(DetailView):
model = Agenda
def get(self, request, *args, **kwargs):
try:
agenda = Agenda.objects.get(id=kwargs.get('pk'))
except Agenda.DoesNotExist:
raise Http404()
if not agenda.can_be_viewed(self.request.user):
raise PermissionDenied()
if agenda.kind == 'meetings':
# redirect to today view
today = datetime.date.today()
return HttpResponseRedirect(reverse('chrono-manager-agenda-day-view',
kwargs={'pk': agenda.id,
'year': today.year,
'month': today.month,
'day': today.day}))
# redirect to settings
return HttpResponseRedirect(
reverse('chrono-manager-agenda-settings', kwargs={'pk': agenda.id}))
agenda_view = AgendaView.as_view()
class AgendaDateView(object):
model = Event
month_format = '%m'
date_field = 'start_datetime'
allow_empty = True
allow_future = True
def dispatch(self, request, *args, **kwargs):
self.agenda = get_object_or_404(Agenda, id=kwargs.get('pk'))
if self.agenda.kind != 'meetings':
raise Http404()
if not self.agenda.can_be_viewed(request.user):
raise PermissionDenied()
# specify 6am time to get the expected timezone on daylight saving time
# days.
try:
self.date = make_aware(datetime.datetime.strptime(
'%s-%s-%s 06:00' % (self.get_year(), self.get_month(), self.get_day()),
'%Y-%m-%d %H:%M'))
except ValueError: # day is out of range for month
# redirect to last day of month
date = datetime.date(int(self.get_year()), int(self.get_month()), 1)
date += datetime.timedelta(days=40)
date = date.replace(day=1)
date -= datetime.timedelta(days=1)
return HttpResponseRedirect(reverse('chrono-manager-agenda-day-view',
kwargs={'pk': self.agenda.id,
'year': date.year,
'month': date.month,
'day': date.day}))
return super(AgendaDateView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(AgendaDateView, self).get_context_data(**kwargs)
context['agenda'] = self.agenda
try:
context['hour_span'] = max(60 // self.agenda.get_base_meeting_duration(), 1)
except ValueError: # no meeting types defined
context['hour_span'] = 1
context['user_can_manage'] = self.agenda.can_be_managed(self.request.user)
return context
def get_queryset(self):
queryset = super(AgendaDateView, self).get_queryset()
queryset = queryset.filter(agenda=self.agenda).prefetch_related('booking_set')
return queryset
def get_days(self):
return [str(x) for x in range(1, 32)]
def get_months(self):
return [(str(x), MONTHS[x]) for x in range(1, 13)]
def get_years(self):
year = now().year
return [str(x) for x in range(year-1, year+5)]
class AgendaDayView(AgendaDateView, DayArchiveView):
template_name = 'chrono/manager_agenda_day_view.html'
def get_previous_day_url(self):
previous_day = self.date.date() - datetime.timedelta(days=1)
return reverse('chrono-manager-agenda-day-view',
kwargs={'pk': self.agenda.id,
'year': previous_day.year,
'month': previous_day.month,
'day': previous_day.day})
def get_next_day_url(self):
next_day = self.date.date() + datetime.timedelta(days=1)
return reverse('chrono-manager-agenda-day-view',
kwargs={'pk': self.agenda.id,
'year': next_day.year,
'month': next_day.month,
'day': next_day.day})
def get_timetable_infos(self):
timeperiods = TimePeriod.objects.filter(
desk__agenda=self.agenda,
weekday=self.date.weekday(),
)
if not timeperiods:
return
min_timeperiod = min([x.start_time for x in timeperiods])
max_timeperiod = max([x.end_time for x in timeperiods])
interval = datetime.timedelta(minutes=60)
current_date = self.date.replace(hour=min_timeperiod.hour, minute=0)
start_date = current_date
max_date = self.date.replace(hour=max_timeperiod.hour, minute=0)
if max_timeperiod.minute != 0:
# until the end of the last hour.
max_date += datetime.timedelta(hours=1)
desks = self.agenda.desk_set.all()
first = True
while current_date < max_date:
# for each timeslot return the timeslot date and a list of per-desk
# bookings
infos = [] # various infos, for each desk
for desk in desks:
info = {'desk': desk}
if first:
# use first row to include opening hours
info['opening_hours'] = opening_hours = []
for opening_hour in desk.get_opening_hours(current_date.date()):
opening_hours.append({
'css_top': 100 * (opening_hour.begin - start_date).seconds // 3600,
'css_height': 100 * (opening_hour.end - opening_hour.begin).seconds // 3600,
})
infos.append(info)
info['bookings'] = bookings = [] # bookings for this desk
finish_datetime = current_date + interval
for event in [x for x in self.object_list if x.desk_id == desk.id and
x.start_datetime >= current_date and x.start_datetime < finish_datetime]:
# don't consider cancelled bookings
for booking in [x for x in event.booking_set.all() if not x.cancellation_datetime]:
booking.css_top = int(100 * event.start_datetime.minute / 60)
booking.css_height = int(100 * event.meeting_type.duration / 60)
bookings.append(booking)
yield current_date, infos
current_date += interval
first = False
agenda_day_view = AgendaDayView.as_view()
class AgendaMonthView(AgendaDateView, MonthArchiveView):
template_name = 'chrono/manager_agenda_month_view.html'
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 = TimePeriod.objects.filter(desk__agenda=self.agenda)
if not timeperiods:
return
first_week_number = self.date.isocalendar()[1]
last_month_day = self.get_next_month(self.date.date()) - datetime.timedelta(days=1)
last_week_number = last_month_day.isocalendar()[1]
if last_week_number < first_week_number: # new year
last_week_number = 53
for week_number in range(first_week_number, last_week_number + 1):
yield self.get_week_timetable_infos(week_number-first_week_number, timeperiods)
def get_week_timetable_infos(self, week_index, timeperiods):
date = self.date + datetime.timedelta(week_index*7)
year, week_number, dow = date.isocalendar()
start_date = date - datetime.timedelta(dow)
self.min_timeperiod = min([x.start_time for x in timeperiods])
self.max_timeperiod = max([x.end_time for x in timeperiods])
interval = datetime.timedelta(minutes=60)
period = self.date.replace(hour=self.min_timeperiod.hour, minute=0)
max_date = self.date.replace(hour=self.max_timeperiod.hour, minute=0)
periods = []
while period < max_date:
periods.append(period)
period = period + interval
return {'days': [self.get_day_timetable_infos(start_date + datetime.timedelta(i), interval) for i in range(1, 8)],
'periods': periods}
def get_day_timetable_infos(self, day, interval):
period = current_date = day.replace(hour=self.min_timeperiod.hour, minute=0)
timetable = {'date': current_date,
'other_month': day.month != self.date.month,
'infos': {'opening_hours': [], 'booked_slots': []}}
desks = self.agenda.desk_set.all()
desks_len = len(desks)
max_date = day.replace(hour=self.max_timeperiod.hour, minute=0)
# compute booking and opening hours only for current month
if self.date.month != day.month:
return timetable
while period <= max_date:
left = 1
period_end = period + interval
for desk_index, desk in enumerate(desks):
width = (98.0 / desks_len) - 1
for event in [x for x in self.object_list if x.desk_id == desk.id and
x.start_datetime >= period and x.start_datetime < period_end]:
# don't consider cancelled bookings
bookings = [x for x in event.booking_set.all() if not x.cancellation_datetime]
if not bookings:
continue
booking = {'css_top': 100 * (event.start_datetime - current_date).seconds // 3600,
'css_height': 100 * event.meeting_type.duration // 60,
'css_width': width,
'css_left': left,
'desk': desk,
'booking': bookings[0]
}
timetable['infos']['booked_slots'].append(booking)
# get desks opening hours on last period iteration
if period == max_date:
for hour in desk.get_opening_hours(current_date):
timetable['infos']['opening_hours'].append({
'css_top': 100 * (hour.begin - current_date).seconds // 3600,
'css_height': 100 * (hour.end - hour.begin).seconds // 3600,
'css_width': width,
'css_left': left,
})
left += width + 1
period += interval
return timetable
agenda_monthly_view = AgendaMonthView.as_view()
class ManagedAgendaMixin(object):
agenda = None
def dispatch(self, request, *args, **kwargs):
try:
self.agenda = Agenda.objects.get(id=kwargs.get('pk'))
except Agenda.DoesNotExist:
raise Http404()
if not self.agenda.can_be_managed(request.user):
raise PermissionDenied()
return super(ManagedAgendaMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ManagedAgendaMixin, self).get_context_data(**kwargs)
context['agenda'] = self.agenda
return context
def get_initial(self):
initial = super(ManagedAgendaMixin, self).get_initial()
initial['agenda'] = self.agenda.id
return initial
def get_success_url(self):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id})
class ManagedAgendaSubobjectMixin(object):
agenda = None
def dispatch(self, request, *args, **kwargs):
self.agenda = self.get_object().agenda
if not self.agenda.can_be_managed(request.user):
raise PermissionDenied()
return super(ManagedAgendaSubobjectMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ManagedAgendaSubobjectMixin, self).get_context_data(**kwargs)
context['agenda'] = self.object.agenda
return context
def get_success_url(self):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.id})
class ManagedDeskMixin(object):
desk = None
def dispatch(self, request, *args, **kwargs):
try:
self.desk = Desk.objects.get(id=kwargs.get('pk'))
except Desk.DoesNotExist:
raise Http404()
if not self.desk.agenda.can_be_managed(request.user):
raise PermissionDenied()
return super(ManagedDeskMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ManagedDeskMixin, self).get_context_data(**kwargs)
context['desk'] = self.desk
context['agenda'] = self.desk.agenda
return context
def get_initial(self):
initial = super(ManagedDeskMixin, self).get_initial()
initial['desk'] = self.desk
return initial
def get_success_url(self):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id})
class ManagedDeskSubobjectMixin(object):
desk = None
def dispatch(self, request, *args, **kwargs):
self.desk = self.get_object().desk
if not self.desk.agenda.can_be_managed(request.user):
raise PermissionDenied()
return super(ManagedDeskSubobjectMixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ManagedDeskSubobjectMixin, self).get_context_data(**kwargs)
context['desk'] = self.object.desk
context['agenda'] = self.object.desk.agenda
return context
def get_success_url(self):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda.id})
class AgendaSettings(ManagedAgendaMixin, DetailView):
template_name = 'chrono/manager_agenda_settings.html'
model = Agenda
def dispatch(self, request, *args, **kwargs):
try:
self.agenda = Agenda.objects.get(id=kwargs.get('pk'))
except Agenda.DoesNotExist:
raise Http404()
if not self.agenda.can_be_managed(request.user):
# "events" agendas settings page can be access by user with the
# view permission as there are no other "view" page for this type
# of agenda.
if self.agenda.kind != 'events' or not self.agenda.can_be_viewed(request.user):
raise PermissionDenied()
return super(DetailView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(AgendaSettings, self).get_context_data(**kwargs)
context['user_can_manage'] = self.get_object().can_be_managed(self.request.user)
return context
agenda_settings = AgendaSettings.as_view()
class AgendaExport(ManagedAgendaMixin, DetailView):
model = Agenda
def get(self, request, *args, **kwargs):
response = HttpResponse(content_type='application/json')
json.dump({'agendas': [self.get_object().export_json()]}, response, indent=2)
return response
agenda_export = AgendaExport.as_view()
class AgendaAddEventView(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_event_form.html'
model = Event
form_class = EventForm
agenda_add_event = AgendaAddEventView.as_view()
class AgendaImportEventsSampleView(TemplateView):
template_name = 'chrono/manager_sample_events.csv'
content_type = 'text/csv'
agenda_import_events_sample_csv = AgendaImportEventsSampleView.as_view()
class AgendaImportEventsView(ManagedAgendaMixin, FormView):
form_class = ImportEventsForm
template_name = 'chrono/manager_import_events.html'
agenda = None
def form_valid(self, form):
if form.events:
for event in form.events:
event.agenda_id = self.kwargs['pk']
event.save()
messages.info(self.request, _('%d events have been imported.') % len(form.events))
return super(AgendaImportEventsView, self).form_valid(form)
agenda_import_events = AgendaImportEventsView.as_view()
class EventEditView(ManagedAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_event_form.html'
model = Event
form_class = EventForm
event_edit = EventEditView.as_view()
class EventDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = Event
def get_context_data(self, **kwargs):
context = super(EventDeleteView, self).get_context_data(**kwargs)
context['cannot_delete'] = bool(
self.object.booking_set.filter(cancellation_datetime__isnull=True).exists() and
self.object.start_datetime > now())
return context
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data()
if context['cannot_delete']:
raise PermissionDenied()
return super(EventDeleteView, self).delete(request, *args, **kwargs)
event_delete = EventDeleteView.as_view()
class AgendaAddMeetingTypeView(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_meeting_type_form.html'
model = Event
form_class = NewMeetingTypeForm
agenda_add_meeting_type = AgendaAddMeetingTypeView.as_view()
class MeetingTypeEditView(ManagedAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_meeting_type_form.html'
model = MeetingType
form_class = MeetingTypeForm
meeting_type_edit = MeetingTypeEditView.as_view()
class MeetingTypeDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = MeetingType
meeting_type_delete = MeetingTypeDeleteView.as_view()
class AgendaAddTimePeriodView(ManagedDeskMixin, FormView):
template_name = 'chrono/manager_time_period_form.html'
form_class = TimePeriodAddForm
def form_valid(self, form):
for weekday in form.cleaned_data.get('weekdays'):
period = TimePeriod(
weekday=weekday,
start_time=form.cleaned_data['start_time'],
end_time=form.cleaned_data['end_time'],
desk=self.desk)
period.save()
return super(AgendaAddTimePeriodView, self).form_valid(form)
agenda_add_time_period = AgendaAddTimePeriodView.as_view()
class TimePeriodEditView(ManagedDeskSubobjectMixin, UpdateView):
template_name = 'chrono/manager_time_period_form.html'
model = TimePeriod
form_class = TimePeriodForm
time_period_edit = TimePeriodEditView.as_view()
class TimePeriodDeleteView(ManagedDeskSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = TimePeriod
time_period_delete = TimePeriodDeleteView.as_view()
class AgendaAddDesk(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_desk_form.html'
model = Desk
form_class = NewDeskForm
agenda_add_desk = AgendaAddDesk.as_view()
class DeskEditView(ManagedAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_desk_form.html'
model = Desk
form_class = DeskForm
desk_edit = DeskEditView.as_view()
class DeskDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = Desk
def get_context_data(self, **kwargs):
context = super(DeskDeleteView, self).get_context_data(**kwargs)
context['cannot_delete'] = Booking.objects.filter(
event__desk=self.get_object(),
event__start_datetime__gt=now(),
cancellation_datetime__isnull=True).exists()
return context
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data()
if context['cannot_delete']:
raise PermissionDenied()
return super(DeskDeleteView, self).delete(request, *args, **kwargs)
desk_delete = DeskDeleteView.as_view()
class AgendaAddTimePeriodExceptionView(ManagedDeskMixin, CreateView):
template_name = 'chrono/manager_time_period_exception_form.html'
model = TimePeriodException
form_class = TimePeriodExceptionForm
agenda_add_time_period_exception = AgendaAddTimePeriodExceptionView.as_view()
class TimePeriodExceptionEditView(ManagedDeskSubobjectMixin, UpdateView):
template_name = 'chrono/manager_time_period_exception_form.html'
model = TimePeriodException
form_class = TimePeriodExceptionForm
time_period_exception_edit = TimePeriodExceptionEditView.as_view()
class TimePeriodExceptionListView(ManagedDeskMixin, ListView):
template_name = 'chrono/manager_time_period_exception_list.html'
model = TimePeriodException
def get_queryset(self):
return self.model.objects.filter(desk=self.desk)
def get_context_data(self, **kwargs):
context = super(TimePeriodExceptionListView, self).get_context_data(**kwargs)
context['user_can_manage'] = self.desk.agenda.can_be_managed(self.request.user)
return context
time_period_exception_list = TimePeriodExceptionListView.as_view()
class TimePeriodExceptionDeleteView(ManagedDeskSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = TimePeriodException
time_period_exception_delete = TimePeriodExceptionDeleteView.as_view()
class DeskImportTimePeriodExceptionsView(ManagedAgendaSubobjectMixin, UpdateView):
model = Desk
form_class = ExceptionsImportForm
template_name = 'chrono/manager_import_exceptions.html'
def get_initial(self):
return {'ics_url': self.get_object().timeperiod_exceptions_remote_url}
def form_valid(self, form):
exceptions = None
try:
if form.cleaned_data['ics_file']:
ics_file_content = force_text(form.cleaned_data['ics_file'].read())
exceptions = form.instance.create_timeperiod_exceptions_from_ics(ics_file_content)
elif form.cleaned_data['ics_url']:
exceptions = form.instance.create_timeperiod_exceptions_from_remote_ics(form.cleaned_data['ics_url'])
else:
form.instance.remove_timeperiod_exceptions_from_remote_ics()
except ICSError as e:
form.add_error(None, force_text(e))
return self.form_invalid(form)
form.instance.timeperiod_exceptions_remote_url = form.cleaned_data['ics_url']
form.instance.save()
if exceptions is not None:
message = ungettext('An exception has been imported.',
'%(count)d exceptions have been imported.', exceptions)
message = message % {'count': exceptions}
messages.info(self.request, message)
return super(DeskImportTimePeriodExceptionsView, self).form_valid(form)
desk_import_time_period_exceptions = DeskImportTimePeriodExceptionsView.as_view()
def menu_json(request):
response = HttpResponse(content_type='application/json')
label = _('Agendas')
json_str = json.dumps([{'label': force_text(label),
'slug': 'calendar',
'url': request.build_absolute_uri(reverse('chrono-manager-homepage'))
}])
for variable in ('jsonpCallback', 'callback'):
if variable in request.GET:
json_str = '%s(%s);' % (request.GET[variable], json_str)
break
response.write(json_str)
return response