2016-02-13 15:31:28 +01:00
|
|
|
# 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/>.
|
|
|
|
|
2018-03-25 11:26:47 +02:00
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
2016-09-16 15:04:02 +02:00
|
|
|
import csv
|
|
|
|
import datetime
|
|
|
|
|
2016-02-13 15:31:28 +01:00
|
|
|
from django import forms
|
2018-03-03 10:47:08 +01:00
|
|
|
from django.contrib.auth.models import Group
|
2020-06-11 17:04:58 +02:00
|
|
|
from django.core.exceptions import FieldDoesNotExist
|
2016-09-16 15:04:02 +02:00
|
|
|
from django.forms import ValidationError
|
2018-03-25 11:26:47 +02:00
|
|
|
from django.utils.encoding import force_text
|
2020-07-10 09:16:01 +02:00
|
|
|
from django.utils.six import StringIO
|
2017-12-30 15:48:48 +01:00
|
|
|
from django.utils.timezone import make_aware
|
2016-09-16 15:04:02 +02:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2016-02-13 15:31:28 +01:00
|
|
|
|
2019-12-16 16:21:24 +01:00
|
|
|
from chrono.agendas.models import (
|
|
|
|
Agenda,
|
|
|
|
Event,
|
|
|
|
MeetingType,
|
|
|
|
TimePeriod,
|
|
|
|
Desk,
|
|
|
|
TimePeriodException,
|
2019-12-12 10:56:14 +01:00
|
|
|
TimePeriodExceptionSource,
|
2020-02-18 12:14:36 +01:00
|
|
|
VirtualMember,
|
2020-05-15 16:52:58 +02:00
|
|
|
Resource,
|
2019-12-16 16:21:24 +01:00
|
|
|
WEEKDAYS_LIST,
|
|
|
|
)
|
2016-02-13 15:31:28 +01:00
|
|
|
|
2016-07-22 14:57:11 +02:00
|
|
|
from . import widgets
|
2019-12-24 16:34:01 +01:00
|
|
|
from .widgets import DateTimeWidget
|
2016-02-13 15:31:28 +01:00
|
|
|
|
|
|
|
|
2018-03-03 10:47:08 +01:00
|
|
|
class AgendaAddForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = Agenda
|
|
|
|
fields = ['label', 'kind', 'edit_role', 'view_role']
|
|
|
|
|
|
|
|
edit_role = forms.ModelChoiceField(
|
2019-12-16 16:21:24 +01:00
|
|
|
label=_('Edit Role'), required=False, queryset=Group.objects.all().order_by('name')
|
|
|
|
)
|
2018-03-03 10:47:08 +01:00
|
|
|
view_role = forms.ModelChoiceField(
|
2019-12-16 16:21:24 +01:00
|
|
|
label=_('View Role'), required=False, queryset=Group.objects.all().order_by('name')
|
|
|
|
)
|
2018-03-03 10:47:08 +01:00
|
|
|
|
|
|
|
|
|
|
|
class AgendaEditForm(AgendaAddForm):
|
|
|
|
class Meta:
|
|
|
|
model = Agenda
|
2020-07-09 14:50:36 +02:00
|
|
|
fields = [
|
|
|
|
'label',
|
|
|
|
'slug',
|
|
|
|
'edit_role',
|
|
|
|
'view_role',
|
|
|
|
'minimal_booking_delay',
|
|
|
|
'maximal_booking_delay',
|
|
|
|
'default_view',
|
|
|
|
]
|
2018-03-03 10:47:08 +01:00
|
|
|
|
2020-02-25 08:38:25 +01:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(AgendaEditForm, self).__init__(*args, **kwargs)
|
|
|
|
if kwargs['instance'].kind != 'virtual':
|
|
|
|
self.fields['minimal_booking_delay'].required = True
|
|
|
|
self.fields['maximal_booking_delay'].required = True
|
2020-07-09 14:50:36 +02:00
|
|
|
if kwargs['instance'].kind != 'events':
|
|
|
|
del self.fields['default_view']
|
2020-02-25 08:38:25 +01:00
|
|
|
|
2018-03-03 10:47:08 +01:00
|
|
|
|
2020-05-15 16:52:58 +02:00
|
|
|
class ResourceAddForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = Resource
|
|
|
|
fields = ['label', 'description']
|
|
|
|
|
|
|
|
|
|
|
|
class ResourceEditForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = Resource
|
|
|
|
fields = ['label', 'slug', 'description']
|
|
|
|
|
|
|
|
|
2019-11-06 15:20:07 +01:00
|
|
|
class NewEventForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = Event
|
|
|
|
widgets = {
|
2019-12-16 16:21:24 +01:00
|
|
|
'agenda': forms.HiddenInput(),
|
|
|
|
'start_datetime': DateTimeWidget(),
|
2020-05-12 14:22:52 +02:00
|
|
|
'publication_date': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
|
2019-11-06 15:20:07 +01:00
|
|
|
}
|
2020-05-15 14:32:37 +02:00
|
|
|
exclude = ['full', 'meeting_type', 'desk', 'slug', 'resources']
|
2019-11-06 15:20:07 +01:00
|
|
|
|
|
|
|
|
2016-02-13 15:31:28 +01:00
|
|
|
class EventForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = Event
|
|
|
|
widgets = {
|
2019-12-16 16:21:24 +01:00
|
|
|
'agenda': forms.HiddenInput(),
|
|
|
|
'start_datetime': DateTimeWidget(),
|
2020-05-12 14:22:52 +02:00
|
|
|
'publication_date': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
|
2016-02-13 15:31:28 +01:00
|
|
|
}
|
2020-05-15 14:32:37 +02:00
|
|
|
exclude = ['full', 'meeting_type', 'desk', 'resources']
|
2016-09-11 11:31:29 +02:00
|
|
|
|
|
|
|
|
2020-05-18 16:45:35 +02:00
|
|
|
class AgendaResourceForm(forms.Form):
|
|
|
|
resource = forms.ModelChoiceField(label=_('Resource'), queryset=Resource.objects.none())
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.fields['resource'].queryset = Resource.objects.exclude(agenda=self.initial['agenda'])
|
|
|
|
|
|
|
|
|
2016-10-28 19:06:51 +02:00
|
|
|
class NewMeetingTypeForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = MeetingType
|
|
|
|
widgets = {
|
2019-12-16 16:21:24 +01:00
|
|
|
'agenda': forms.HiddenInput(),
|
2016-10-28 19:06:51 +02:00
|
|
|
}
|
2020-06-17 17:39:26 +02:00
|
|
|
exclude = ['slug', 'deleted']
|
2016-10-28 19:06:51 +02:00
|
|
|
|
2020-02-18 12:14:36 +01:00
|
|
|
def clean(self):
|
|
|
|
super().clean()
|
|
|
|
agenda = self.cleaned_data['agenda']
|
|
|
|
for virtual_agenda in agenda.virtual_agendas.all():
|
|
|
|
for real_agenda in virtual_agenda.real_agendas.all():
|
|
|
|
if real_agenda != agenda:
|
|
|
|
raise ValidationError(
|
|
|
|
_("Can't add a meetingtype to an agenda that is included in a virtual agenda.")
|
|
|
|
)
|
|
|
|
|
2016-10-28 19:06:51 +02:00
|
|
|
|
2016-09-11 11:31:29 +02:00
|
|
|
class MeetingTypeForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = MeetingType
|
|
|
|
widgets = {
|
2019-12-16 16:21:24 +01:00
|
|
|
'agenda': forms.HiddenInput(),
|
2016-09-11 11:31:29 +02:00
|
|
|
}
|
2020-06-17 17:39:26 +02:00
|
|
|
exclude = ['deleted']
|
2016-09-11 11:31:29 +02:00
|
|
|
|
2020-02-18 12:14:36 +01:00
|
|
|
def clean(self):
|
|
|
|
super().clean()
|
|
|
|
for virtual_agenda in self.instance.agenda.virtual_agendas.all():
|
|
|
|
if virtual_agenda.real_agendas.count() == 1:
|
|
|
|
continue
|
|
|
|
for mt in virtual_agenda.iter_meetingtypes():
|
|
|
|
if (
|
|
|
|
mt.label == self.instance.label
|
|
|
|
and mt.slug == self.instance.slug
|
|
|
|
and mt.duration == self.instance.duration
|
|
|
|
):
|
|
|
|
raise ValidationError(
|
|
|
|
_('This meetingtype is used by a virtual agenda: %s' % virtual_agenda)
|
|
|
|
)
|
|
|
|
|
2016-09-11 11:31:29 +02:00
|
|
|
|
2018-09-22 18:12:22 +02:00
|
|
|
class TimePeriodAddForm(forms.Form):
|
|
|
|
weekdays = forms.MultipleChoiceField(
|
2019-12-16 16:21:24 +01:00
|
|
|
label=_('Days'), widget=widgets.WeekdaysWidget(), choices=WEEKDAYS_LIST
|
|
|
|
)
|
2018-09-22 18:12:22 +02:00
|
|
|
start_time = forms.TimeField(label=_('Start Time'), widget=widgets.TimeWidget())
|
|
|
|
end_time = forms.TimeField(label=_('End Time'), widget=widgets.TimeWidget())
|
|
|
|
|
2019-03-14 14:31:48 +01:00
|
|
|
def clean_end_time(self):
|
|
|
|
if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']:
|
|
|
|
raise ValidationError(_('End time must come after start time.'))
|
|
|
|
return self.cleaned_data['end_time']
|
|
|
|
|
2018-09-22 18:12:22 +02:00
|
|
|
|
2016-09-11 11:31:29 +02:00
|
|
|
class TimePeriodForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = TimePeriod
|
|
|
|
widgets = {
|
2019-12-16 16:21:24 +01:00
|
|
|
'start_time': widgets.TimeWidget(),
|
|
|
|
'end_time': widgets.TimeWidget(),
|
|
|
|
'desk': forms.HiddenInput(),
|
2020-02-26 11:52:05 +01:00
|
|
|
'agenda': forms.HiddenInput(),
|
2017-09-01 15:01:07 +02:00
|
|
|
}
|
|
|
|
exclude = []
|
|
|
|
|
2020-02-26 11:52:05 +01:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
has_desk = kwargs.pop('has_desk')
|
|
|
|
super(TimePeriodForm, self).__init__(*args, **kwargs)
|
|
|
|
if has_desk:
|
|
|
|
del self.fields['agenda']
|
|
|
|
else:
|
|
|
|
del self.fields['desk']
|
|
|
|
|
2019-03-14 14:31:48 +01:00
|
|
|
def clean_end_time(self):
|
|
|
|
if self.cleaned_data['end_time'] <= self.cleaned_data['start_time']:
|
|
|
|
raise ValidationError(_('End time must come after start time.'))
|
|
|
|
return self.cleaned_data['end_time']
|
|
|
|
|
2017-09-01 15:01:07 +02:00
|
|
|
|
|
|
|
class NewDeskForm(forms.ModelForm):
|
2020-05-07 17:45:01 +02:00
|
|
|
copy_from = forms.ModelChoiceField(
|
|
|
|
label=_('Copy settings of desk'), required=False, queryset=Desk.objects.none(),
|
|
|
|
)
|
|
|
|
|
2017-09-01 15:01:07 +02:00
|
|
|
class Meta:
|
|
|
|
model = Desk
|
|
|
|
widgets = {
|
|
|
|
'agenda': forms.HiddenInput(),
|
|
|
|
}
|
2019-12-10 15:28:39 +01:00
|
|
|
exclude = ['slug']
|
2017-09-01 15:01:07 +02:00
|
|
|
|
2020-05-07 17:45:01 +02:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.fields['copy_from'].queryset = Desk.objects.filter(agenda=self.initial['agenda'])
|
|
|
|
|
|
|
|
def save(self):
|
|
|
|
if self.cleaned_data['copy_from']:
|
|
|
|
return self.cleaned_data['copy_from'].duplicate(label=self.cleaned_data['label'])
|
|
|
|
return super().save()
|
|
|
|
|
2017-09-01 15:01:07 +02:00
|
|
|
|
|
|
|
class DeskForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = Desk
|
|
|
|
widgets = {
|
|
|
|
'agenda': forms.HiddenInput(),
|
2016-09-11 11:31:29 +02:00
|
|
|
}
|
2019-12-10 15:28:39 +01:00
|
|
|
exclude = []
|
2016-09-16 15:04:02 +02:00
|
|
|
|
|
|
|
|
2017-08-24 14:15:18 +02:00
|
|
|
class TimePeriodExceptionForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = TimePeriodException
|
|
|
|
fields = ['desk', 'start_datetime', 'end_datetime', 'label']
|
|
|
|
widgets = {
|
|
|
|
'desk': forms.HiddenInput(),
|
|
|
|
'start_datetime': DateTimeWidget(),
|
|
|
|
'end_datetime': DateTimeWidget(),
|
|
|
|
}
|
|
|
|
|
2020-05-05 14:59:24 +02:00
|
|
|
def clean(self):
|
|
|
|
cleaned_data = super().clean()
|
|
|
|
|
|
|
|
if 'start_datetime' in cleaned_data and 'end_datetime' in cleaned_data:
|
|
|
|
if cleaned_data['end_datetime'] <= cleaned_data['start_datetime']:
|
|
|
|
self.add_error('end_datetime', _('End datetime must be greater than start datetime.'))
|
|
|
|
|
|
|
|
return cleaned_data
|
2017-08-24 14:15:18 +02:00
|
|
|
|
|
|
|
|
2020-02-18 12:14:36 +01:00
|
|
|
class VirtualMemberForm(forms.ModelForm):
|
|
|
|
class Meta:
|
|
|
|
model = VirtualMember
|
|
|
|
fields = ['virtual_agenda', 'real_agenda']
|
|
|
|
widgets = {
|
|
|
|
'virtual_agenda': forms.HiddenInput(),
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(VirtualMemberForm, self).__init__(*args, **kwargs)
|
|
|
|
self.fields['real_agenda'].queryset = Agenda.objects.filter(kind='meetings').exclude(
|
|
|
|
virtual_agendas__pk__in=[kwargs['initial']['agenda']]
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2016-09-16 15:04:02 +02:00
|
|
|
class ImportEventsForm(forms.Form):
|
2017-05-05 15:56:33 +02:00
|
|
|
events_csv_file = forms.FileField(
|
2019-12-16 16:21:24 +01:00
|
|
|
label=_('Events File'),
|
|
|
|
required=True,
|
|
|
|
help_text=_(
|
|
|
|
'CSV file with date, time, number of places, '
|
2020-01-20 20:03:51 +01:00
|
|
|
'number of places in waiting list, label, and '
|
2020-07-03 09:45:15 +02:00
|
|
|
'optionally, identifier, description, pricing, '
|
|
|
|
'URL, and publication date as columns.'
|
2019-12-16 16:21:24 +01:00
|
|
|
),
|
|
|
|
)
|
2016-09-16 15:22:27 +02:00
|
|
|
events = None
|
2016-09-16 15:04:02 +02:00
|
|
|
|
2019-11-27 09:27:44 +01:00
|
|
|
def __init__(self, agenda_pk, **kwargs):
|
|
|
|
self.agenda_pk = agenda_pk
|
|
|
|
super(ImportEventsForm, self).__init__(**kwargs)
|
|
|
|
|
2016-09-16 15:04:02 +02:00
|
|
|
def clean_events_csv_file(self):
|
|
|
|
content = self.cleaned_data['events_csv_file'].read()
|
2018-03-25 11:26:47 +02:00
|
|
|
if b'\0' in content:
|
2016-09-16 15:04:02 +02:00
|
|
|
raise ValidationError(_('Invalid file format.'))
|
|
|
|
|
2020-06-17 14:29:33 +02:00
|
|
|
for charset in ('utf-8-sig', 'iso-8859-15'):
|
2019-03-25 18:17:06 +01:00
|
|
|
try:
|
|
|
|
content = content.decode(charset)
|
|
|
|
break
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
continue
|
|
|
|
# all byte-sequences are ok for iso-8859-15 so we will always reach
|
|
|
|
# this line with content being a unicode string.
|
|
|
|
|
2016-09-16 15:04:02 +02:00
|
|
|
try:
|
2019-09-21 22:36:08 +02:00
|
|
|
dialect = csv.Sniffer().sniff(content)
|
2016-09-16 15:22:27 +02:00
|
|
|
except csv.Error:
|
2016-09-16 15:04:02 +02:00
|
|
|
dialect = None
|
|
|
|
|
|
|
|
events = []
|
2020-06-11 17:54:24 +02:00
|
|
|
slugs = set()
|
2020-07-10 09:16:01 +02:00
|
|
|
for i, csvline in enumerate(csv.reader(StringIO(content), dialect=dialect)):
|
2016-09-16 15:04:02 +02:00
|
|
|
if not csvline:
|
|
|
|
continue
|
2016-09-16 15:27:03 +02:00
|
|
|
if len(csvline) < 3:
|
2019-12-16 16:21:24 +01:00
|
|
|
raise ValidationError(_('Invalid file format. (line %d)') % (i + 1))
|
2016-09-16 15:28:09 +02:00
|
|
|
if i == 0 and csvline[0].strip('#') in ('date', 'Date', _('date'), _('Date')):
|
2016-09-16 15:04:02 +02:00
|
|
|
continue
|
|
|
|
event = Event()
|
2019-11-27 09:27:44 +01:00
|
|
|
event.agenda_id = self.agenda_pk
|
2019-12-16 16:21:24 +01:00
|
|
|
for datetime_fmt in (
|
|
|
|
'%Y-%m-%d %H:%M',
|
|
|
|
'%d/%m/%Y %H:%M',
|
|
|
|
'%d/%m/%Y %Hh%M',
|
|
|
|
'%Y-%m-%d %H:%M:%S',
|
|
|
|
'%d/%m/%Y %H:%M:%S',
|
|
|
|
):
|
2016-09-16 15:04:02 +02:00
|
|
|
try:
|
2019-12-16 16:21:24 +01:00
|
|
|
event_datetime = datetime.datetime.strptime('%s %s' % tuple(csvline[:2]), datetime_fmt)
|
2016-09-16 15:04:02 +02:00
|
|
|
except ValueError:
|
|
|
|
continue
|
2017-12-30 15:48:48 +01:00
|
|
|
event.start_datetime = make_aware(event_datetime)
|
2016-09-16 15:04:02 +02:00
|
|
|
break
|
|
|
|
else:
|
2019-12-16 16:21:24 +01:00
|
|
|
raise ValidationError(_('Invalid file format. (date/time format, line %d)') % (i + 1))
|
2016-09-16 15:04:02 +02:00
|
|
|
try:
|
|
|
|
event.places = int(csvline[2])
|
|
|
|
except ValueError:
|
2019-12-16 16:21:24 +01:00
|
|
|
raise ValidationError(_('Invalid file format. (number of places, line %d)') % (i + 1))
|
2016-09-16 15:04:02 +02:00
|
|
|
if len(csvline) >= 4:
|
|
|
|
try:
|
|
|
|
event.waiting_list_places = int(csvline[3])
|
|
|
|
except ValueError:
|
2019-12-16 16:21:24 +01:00
|
|
|
raise ValidationError(
|
|
|
|
_('Invalid file format. (number of places in waiting list, line %d)') % (i + 1)
|
|
|
|
)
|
2016-09-16 15:04:02 +02:00
|
|
|
if len(csvline) >= 5:
|
2019-11-06 15:20:07 +01:00
|
|
|
event.label = force_text(csvline[4])
|
2019-11-27 09:27:44 +01:00
|
|
|
exclude = ['desk', 'meeting_type']
|
2019-11-06 15:20:07 +01:00
|
|
|
if len(csvline) >= 6:
|
2020-03-02 13:42:25 +01:00
|
|
|
event.slug = force_text(csvline[5]) if csvline[5] else None
|
2020-06-11 17:54:24 +02:00
|
|
|
if event.slug and event.slug in slugs:
|
2020-06-17 13:42:12 +02:00
|
|
|
raise ValidationError(_('File contains duplicated event identifiers: %s') % event.slug)
|
2020-06-11 17:54:24 +02:00
|
|
|
else:
|
|
|
|
slugs.add(event.slug)
|
2019-11-06 15:20:07 +01:00
|
|
|
else:
|
|
|
|
exclude += ['slug']
|
2020-01-20 20:03:51 +01:00
|
|
|
column_index = 7
|
|
|
|
for more_attr in ('description', 'pricing', 'url'):
|
|
|
|
if len(csvline) >= column_index:
|
|
|
|
setattr(event, more_attr, csvline[column_index - 1])
|
|
|
|
column_index += 1
|
|
|
|
|
2020-06-18 10:39:04 +02:00
|
|
|
if len(csvline) >= 10 and csvline[9]: # publication date is optional
|
|
|
|
for date_fmt in ('%Y-%m-%d', '%d/%m/%Y'):
|
|
|
|
try:
|
|
|
|
event.publication_date = datetime.datetime.strptime(csvline[9], date_fmt).date()
|
|
|
|
break
|
|
|
|
except ValueError:
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
raise ValidationError(_('Invalid file format. (date format, line %d)') % (i + 1))
|
|
|
|
|
2020-07-09 16:10:37 +02:00
|
|
|
if len(csvline) >= 11 and csvline[10]: # duration is optional
|
|
|
|
try:
|
|
|
|
event.duration = int(csvline[10])
|
|
|
|
except ValueError:
|
|
|
|
raise ValidationError(_('Invalid file format. (duration, line %d)') % (i + 1))
|
|
|
|
|
2019-07-23 16:30:30 +02:00
|
|
|
try:
|
2019-11-06 15:20:07 +01:00
|
|
|
event.full_clean(exclude=exclude)
|
2019-07-23 16:30:30 +02:00
|
|
|
except ValidationError as e:
|
2020-06-11 17:04:58 +02:00
|
|
|
errors = [_('Invalid file format:\n')]
|
|
|
|
for label, field_errors in e.message_dict.items():
|
|
|
|
label_name = self.get_verbose_name(label)
|
|
|
|
msg = _('%s: ') % label_name if label_name else ''
|
|
|
|
msg += _('%(errors)s (line %(line)d)') % {
|
|
|
|
'errors': ', '.join(field_errors),
|
|
|
|
'line': i + 1,
|
|
|
|
}
|
|
|
|
errors.append(msg)
|
2019-07-23 16:30:30 +02:00
|
|
|
raise ValidationError(errors)
|
2016-09-16 15:04:02 +02:00
|
|
|
events.append(event)
|
|
|
|
self.events = events
|
2017-09-03 13:28:50 +02:00
|
|
|
|
2020-06-11 17:04:58 +02:00
|
|
|
@staticmethod
|
|
|
|
def get_verbose_name(field_name):
|
|
|
|
try:
|
|
|
|
return Event._meta.get_field(field_name).verbose_name
|
|
|
|
except FieldDoesNotExist:
|
|
|
|
return ''
|
|
|
|
|
2017-09-03 13:28:50 +02:00
|
|
|
|
|
|
|
class ExceptionsImportForm(forms.ModelForm):
|
2019-12-16 16:21:24 +01:00
|
|
|
ics_file = forms.FileField(
|
|
|
|
label=_('ICS File'),
|
|
|
|
required=False,
|
2020-06-05 15:14:16 +02:00
|
|
|
help_text=_('ICS file containing events which will be considered as exceptions.'),
|
2019-12-16 16:21:24 +01:00
|
|
|
)
|
|
|
|
ics_url = forms.URLField(
|
|
|
|
label=_('URL'),
|
|
|
|
required=False,
|
|
|
|
help_text=_('URL to remote calendar which will be synchronised hourly.'),
|
|
|
|
)
|
2018-09-22 16:25:46 +02:00
|
|
|
|
2019-12-10 15:28:39 +01:00
|
|
|
class Meta:
|
|
|
|
model = Desk
|
|
|
|
fields = []
|
|
|
|
|
|
|
|
def clean(self, *args, **kwargs):
|
|
|
|
cleaned_data = super().clean(*args, **kwargs)
|
|
|
|
if not cleaned_data.get('ics_file') and not cleaned_data.get('ics_url'):
|
|
|
|
raise forms.ValidationError(_('Please provide an ICS File or an URL.'))
|
|
|
|
|
2018-09-22 16:25:46 +02:00
|
|
|
|
2019-12-12 10:56:14 +01:00
|
|
|
class TimePeriodExceptionSourceReplaceForm(forms.ModelForm):
|
2020-01-28 15:08:24 +01:00
|
|
|
ics_newfile = forms.FileField(
|
2019-12-12 10:56:14 +01:00
|
|
|
label=_('ICS File'),
|
|
|
|
required=False,
|
|
|
|
help_text=_('ICS file containing events which will be considered as exceptions.'),
|
|
|
|
)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = TimePeriodExceptionSource
|
|
|
|
fields = []
|
|
|
|
|
2020-01-28 15:08:24 +01:00
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
if bool(self.instance.ics_file):
|
|
|
|
self.instance.ics_file.delete()
|
|
|
|
self.instance.ics_file = self.cleaned_data['ics_newfile']
|
|
|
|
self.instance.save()
|
|
|
|
|
2019-12-12 10:56:14 +01:00
|
|
|
|
2018-09-22 16:25:46 +02:00
|
|
|
class AgendasImportForm(forms.Form):
|
|
|
|
agendas_json = forms.FileField(label=_('Agendas Export File'))
|
2020-06-17 10:52:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
class AgendaDuplicateForm(forms.Form):
|
|
|
|
label = forms.CharField(label=_('New label'), max_length=150, required=False)
|