agendas: add global exceptions sources (#18904)
This commit is contained in:
parent
7b5da14331
commit
bf394e0a07
|
@ -0,0 +1,27 @@
|
||||||
|
# chrono - agendas system
|
||||||
|
# Copyright (C) 2020 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/>.
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from chrono.agendas.models import Desk
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Synchronize time period exceptions from settings'
|
||||||
|
|
||||||
|
def handle(self, **options):
|
||||||
|
for desk in Desk.objects.all():
|
||||||
|
desk.import_timeperiod_exceptions_from_settings()
|
|
@ -0,0 +1,33 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.18 on 2020-08-31 14:34
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('agendas', '0056_auto_20200811_1611'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='timeperiodexceptionsource', name='enabled', field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='timeperiodexceptionsource',
|
||||||
|
name='last_update',
|
||||||
|
field=models.DateTimeField(auto_now=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='timeperiodexceptionsource',
|
||||||
|
name='settings_label',
|
||||||
|
field=models.CharField(max_length=150, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='timeperiodexceptionsource',
|
||||||
|
name='settings_slug',
|
||||||
|
field=models.CharField(max_length=150, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -39,6 +39,7 @@ from django.utils import functional
|
||||||
from django.utils.dates import WEEKDAYS
|
from django.utils.dates import WEEKDAYS
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.formats import date_format
|
from django.utils.formats import date_format
|
||||||
|
from django.utils.module_loading import import_string
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils.timezone import localtime, now, make_aware, make_naive, is_aware
|
from django.utils.timezone import localtime, now, make_aware, make_naive, is_aware
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||||
|
@ -1070,9 +1071,12 @@ class Desk(models.Model):
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
assert self.agenda.kind != 'virtual', "a desk can't reference a virtual agenda"
|
assert self.agenda.kind != 'virtual', "a desk can't reference a virtual agenda"
|
||||||
|
first_created = not self.pk
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
self.slug = generate_slug(self, agenda=self.agenda)
|
self.slug = generate_slug(self, agenda=self.agenda)
|
||||||
super(Desk, self).save(*args, **kwargs)
|
super(Desk, self).save(*args, **kwargs)
|
||||||
|
if first_created:
|
||||||
|
self.import_timeperiod_exceptions_from_settings(enable=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def base_slug(self):
|
def base_slug(self):
|
||||||
|
@ -1294,6 +1298,24 @@ class Desk(models.Model):
|
||||||
|
|
||||||
return [OpeningHour(*time_range) for time_range in (openslots - exceptions)]
|
return [OpeningHour(*time_range) for time_range in (openslots - exceptions)]
|
||||||
|
|
||||||
|
def import_timeperiod_exceptions_from_settings(self, enable=False):
|
||||||
|
start_update = now()
|
||||||
|
for slug, source_info in settings.EXCEPTIONS_SOURCES.items():
|
||||||
|
label = source_info['label']
|
||||||
|
try:
|
||||||
|
source = TimePeriodExceptionSource.objects.get(desk=self, settings_slug=slug)
|
||||||
|
except TimePeriodExceptionSource.DoesNotExist:
|
||||||
|
source = TimePeriodExceptionSource.objects.create(
|
||||||
|
desk=self, settings_slug=slug, enabled=False
|
||||||
|
)
|
||||||
|
source.settings_label = _(label)
|
||||||
|
source.save()
|
||||||
|
if enable or source.enabled: # if already enabled, update anyway
|
||||||
|
source.enable()
|
||||||
|
TimePeriodExceptionSource.objects.filter(
|
||||||
|
desk=self, settings_slug__isnull=False, last_update__lt=start_update
|
||||||
|
).delete() # source was not in settings anymore
|
||||||
|
|
||||||
|
|
||||||
class Resource(models.Model):
|
class Resource(models.Model):
|
||||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||||
|
@ -1345,10 +1367,16 @@ class TimePeriodExceptionSource(models.Model):
|
||||||
ics_filename = models.CharField(null=True, max_length=256)
|
ics_filename = models.CharField(null=True, max_length=256)
|
||||||
ics_file = models.FileField(upload_to=ics_directory_path, blank=True, null=True)
|
ics_file = models.FileField(upload_to=ics_directory_path, blank=True, null=True)
|
||||||
ics_url = models.URLField(null=True, max_length=500)
|
ics_url = models.URLField(null=True, max_length=500)
|
||||||
|
settings_slug = models.CharField(null=True, max_length=150)
|
||||||
|
settings_label = models.CharField(null=True, max_length=150)
|
||||||
|
last_update = models.DateTimeField(auto_now=True, null=True)
|
||||||
|
enabled = models.BooleanField(default=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.ics_filename is not None:
|
if self.ics_filename is not None:
|
||||||
return self.ics_filename
|
return self.ics_filename
|
||||||
|
if self.settings_label is not None:
|
||||||
|
return ugettext(self.settings_label)
|
||||||
return self.ics_url
|
return self.ics_url
|
||||||
|
|
||||||
def duplicate(self, desk_target=None):
|
def duplicate(self, desk_target=None):
|
||||||
|
@ -1366,6 +1394,34 @@ class TimePeriodExceptionSource(models.Model):
|
||||||
|
|
||||||
return new_source
|
return new_source
|
||||||
|
|
||||||
|
def enable(self):
|
||||||
|
source_info = settings.EXCEPTIONS_SOURCES.get(self.settings_slug)
|
||||||
|
if not source_info:
|
||||||
|
return
|
||||||
|
source_class = import_string(source_info['class'])
|
||||||
|
calendar = source_class()
|
||||||
|
this_year = now().year
|
||||||
|
days = [day for year in range(this_year, this_year + 3) for day in calendar.holidays(year)]
|
||||||
|
with transaction.atomic():
|
||||||
|
self.timeperiodexception_set.all().delete()
|
||||||
|
for day, label in days:
|
||||||
|
start_datetime = make_aware(datetime.datetime.combine(day, datetime.datetime.min.time()))
|
||||||
|
end_datetime = start_datetime + datetime.timedelta(days=1)
|
||||||
|
TimePeriodException.objects.create(
|
||||||
|
desk=self.desk,
|
||||||
|
source=self,
|
||||||
|
label=_(label),
|
||||||
|
start_datetime=start_datetime,
|
||||||
|
end_datetime=end_datetime,
|
||||||
|
)
|
||||||
|
self.enabled = True
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
self.timeperiodexception_set.all().delete()
|
||||||
|
self.enabled = False
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
class TimePeriodException(models.Model):
|
class TimePeriodException(models.Model):
|
||||||
desk = models.ForeignKey(Desk, on_delete=models.CASCADE)
|
desk = models.ForeignKey(Desk, on_delete=models.CASCADE)
|
||||||
|
|
|
@ -17,13 +17,17 @@
|
||||||
<ul class="objects-list single-links">
|
<ul class="objects-list single-links">
|
||||||
{% for object in exception_sources %}
|
{% for object in exception_sources %}
|
||||||
<li>
|
<li>
|
||||||
<a title="{{ object }}" {% if not object.ics_filename %}href="{{ object }}"{% endif %}>{% if object.ics_filename %}{{ object|truncatechars:50 }}{% else %}{{ object|truncatechars:50 }}{% endif %}</a>
|
<a {% if not object.enabled %}class="disabled"{% endif %} title="{{ object }}" {% if object.ics_url %}href="{{ object }}"{% endif %}>{% if object.ics_filename %}{{ object|truncatechars:50 }}{% else %}{{ object|truncatechars:50 }}{% endif %}</a>
|
||||||
{% if object.ics_filename %}
|
{% if object.ics_filename %}
|
||||||
<a rel="popup" class="link-action-icon refresh" href="{% url 'chrono-manager-time-period-exception-source-replace' object.pk %}">{% trans "replace" %}</a>
|
<a rel="popup" class="link-action-icon refresh" href="{% url 'chrono-manager-time-period-exception-source-replace' object.pk %}">{% trans "replace" %}</a>
|
||||||
{% else %}
|
{% elif object.ics_url %}
|
||||||
<a class="link-action-icon refresh" href="{% url 'chrono-manager-time-period-exception-source-refresh' object.pk %}">{% trans "refresh" %}</a>
|
<a class="link-action-icon refresh" href="{% url 'chrono-manager-time-period-exception-source-refresh' object.pk %}">{% trans "refresh" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if not object.settings_slug %}
|
||||||
<a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-exception-source-delete' object.pk %}">{% trans "remove" %}</a>
|
<a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-exception-source-delete' object.pk %}">{% trans "remove" %}</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="link-action-text" href="{% url 'chrono-manager-time-period-exception-source-toggle' object.pk %}">({{ object.enabled|yesno:_("disable,enable") }})</a>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -193,6 +193,11 @@ urlpatterns = [
|
||||||
views.time_period_exception_source_refresh,
|
views.time_period_exception_source_refresh,
|
||||||
name='chrono-manager-time-period-exception-source-refresh',
|
name='chrono-manager-time-period-exception-source-refresh',
|
||||||
),
|
),
|
||||||
|
url(
|
||||||
|
r'^time-period-exceptions-source/(?P<pk>\d+)/toggle$',
|
||||||
|
views.time_period_exception_source_toggle,
|
||||||
|
name='chrono-manager-time-period-exception-source-toggle',
|
||||||
|
),
|
||||||
url(
|
url(
|
||||||
r'^time-period-exceptions-source/(?P<pk>\d+)/replace$',
|
r'^time-period-exceptions-source/(?P<pk>\d+)/replace$',
|
||||||
views.time_period_exception_source_replace,
|
views.time_period_exception_source_replace,
|
||||||
|
|
|
@ -1946,6 +1946,32 @@ class EventCancellationReportListView(ViewableAgendaMixin, ListView):
|
||||||
event_cancellation_report_list = EventCancellationReportListView.as_view()
|
event_cancellation_report_list = EventCancellationReportListView.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
class TimePeriodExceptionSourceToggleView(ManagedDeskSubobjectMixin, DetailView):
|
||||||
|
model = TimePeriodExceptionSource
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
source = super().get_object(queryset)
|
||||||
|
if source.settings_slug is None:
|
||||||
|
raise Http404('This source cannot be enabled nor disabled')
|
||||||
|
return source
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
source = self.get_object()
|
||||||
|
if source.enabled:
|
||||||
|
source.disable()
|
||||||
|
message = _('Exception source %(source)s has been disabled on desk %(desk)s.')
|
||||||
|
else:
|
||||||
|
source.enable()
|
||||||
|
message = _('Exception source %(source)s has been enabled on desk %(desk)s.')
|
||||||
|
messages.info(self.request, message % {'source': source, 'desk': source.desk})
|
||||||
|
return HttpResponseRedirect(
|
||||||
|
reverse('chrono-manager-agenda-settings', kwargs={'pk': source.desk.agenda_id})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
time_period_exception_source_toggle = TimePeriodExceptionSourceToggleView.as_view()
|
||||||
|
|
||||||
|
|
||||||
def menu_json(request):
|
def menu_json(request):
|
||||||
label = _('Agendas')
|
label = _('Agendas')
|
||||||
json_str = json.dumps(
|
json_str = json.dumps(
|
||||||
|
|
|
@ -26,6 +26,8 @@ and to disable DEBUG mode in production.
|
||||||
import os
|
import os
|
||||||
from django.conf.global_settings import STATICFILES_FINDERS
|
from django.conf.global_settings import STATICFILES_FINDERS
|
||||||
|
|
||||||
|
_ = lambda s: s
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
@ -166,6 +168,10 @@ REQUESTS_PROXIES = None
|
||||||
# we use 28s by default: timeout just before web server, which is usually 30s
|
# we use 28s by default: timeout just before web server, which is usually 30s
|
||||||
REQUESTS_TIMEOUT = 28
|
REQUESTS_TIMEOUT = 28
|
||||||
|
|
||||||
|
EXCEPTIONS_SOURCES = {
|
||||||
|
'holidays': {'class': 'workalendar.europe.France', 'label': _('Holidays')},
|
||||||
|
}
|
||||||
|
|
||||||
local_settings_file = os.environ.get(
|
local_settings_file = os.environ.get(
|
||||||
'CHRONO_SETTINGS_FILE', os.path.join(os.path.dirname(__file__), 'local_settings.py')
|
'CHRONO_SETTINGS_FILE', os.path.join(os.path.dirname(__file__), 'local_settings.py')
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0 0 1 1 * chrono /usr/bin/chrono-manage -- tenant_command sync_desks_timeperiod_exceptions_from_settings --all-tenants
|
1
setup.py
1
setup.py
|
@ -168,6 +168,7 @@ setup(
|
||||||
'vobject',
|
'vobject',
|
||||||
'python-dateutil',
|
'python-dateutil',
|
||||||
'requests',
|
'requests',
|
||||||
|
'workalendar',
|
||||||
],
|
],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
cmdclass={
|
cmdclass={
|
||||||
|
|
|
@ -25,3 +25,5 @@ KNOWN_SERVICES = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXCEPTIONS_SOURCES = {}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import requests
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
|
from django.test import override_settings
|
||||||
from django.utils.timezone import localtime, make_aware, now
|
from django.utils.timezone import localtime, make_aware, now
|
||||||
|
|
||||||
from chrono.agendas.models import (
|
from chrono.agendas.models import (
|
||||||
|
@ -512,6 +513,75 @@ def test_sync_desks_timeperiod_exceptions_from_ics(mocked_get, capsys):
|
||||||
assert import_file_ics.call_args_list == []
|
assert import_file_ics.call_args_list == []
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(
|
||||||
|
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||||
|
)
|
||||||
|
def test_timeperiodexception_from_settings():
|
||||||
|
agenda = Agenda(label=u'Test 1 agenda')
|
||||||
|
agenda.save()
|
||||||
|
desk = Desk(label='Test 1 desk', agenda=agenda)
|
||||||
|
desk.save()
|
||||||
|
|
||||||
|
# first save automatically load exceptions
|
||||||
|
source = TimePeriodExceptionSource.objects.get(desk=desk)
|
||||||
|
assert source.settings_slug == 'holidays'
|
||||||
|
assert source.enabled
|
||||||
|
assert TimePeriodException.objects.filter(desk=desk, source=source).exists()
|
||||||
|
|
||||||
|
exception = TimePeriodException.objects.first()
|
||||||
|
from workalendar.europe import France
|
||||||
|
|
||||||
|
date, label = France().holidays()[0]
|
||||||
|
exception = TimePeriodException.objects.filter(label=label).first()
|
||||||
|
assert exception.end_datetime - exception.start_datetime == datetime.timedelta(days=1)
|
||||||
|
assert localtime(exception.start_datetime).date() == date
|
||||||
|
|
||||||
|
source.disable()
|
||||||
|
assert not source.enabled
|
||||||
|
assert not TimePeriodException.objects.filter(desk=desk, source=source).exists()
|
||||||
|
|
||||||
|
source.enable()
|
||||||
|
assert source.enabled
|
||||||
|
assert TimePeriodException.objects.filter(desk=desk, source=source).exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_timeperiodexception_from_settings_command():
|
||||||
|
setting = {
|
||||||
|
'EXCEPTIONS_SOURCES': {'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||||
|
}
|
||||||
|
agenda = Agenda(label=u'Test 1 agenda')
|
||||||
|
agenda.save()
|
||||||
|
desk1 = Desk(label='Test 1 desk', agenda=agenda)
|
||||||
|
desk1.save()
|
||||||
|
with override_settings(**setting):
|
||||||
|
desk2 = Desk(label='Test 2 desk', agenda=agenda)
|
||||||
|
desk2.save()
|
||||||
|
desk3 = Desk(label='Test 3 desk', agenda=agenda)
|
||||||
|
desk3.save()
|
||||||
|
source3 = TimePeriodExceptionSource.objects.get(desk=desk3)
|
||||||
|
source3.disable()
|
||||||
|
|
||||||
|
call_command('sync_desks_timeperiod_exceptions_from_settings')
|
||||||
|
assert not TimePeriodExceptionSource.objects.get(desk=desk1).enabled
|
||||||
|
source2 = TimePeriodExceptionSource.objects.get(desk=desk2)
|
||||||
|
assert source2.enabled
|
||||||
|
source3.refresh_from_db()
|
||||||
|
assert not source3.enabled
|
||||||
|
|
||||||
|
exceptions_count = source2.timeperiodexception_set.count()
|
||||||
|
# Alsace Moselle has more holidays
|
||||||
|
setting['EXCEPTIONS_SOURCES']['holidays']['class'] = 'workalendar.europe.FranceAlsaceMoselle'
|
||||||
|
with override_settings(**setting):
|
||||||
|
call_command('sync_desks_timeperiod_exceptions_from_settings')
|
||||||
|
source2.refresh_from_db()
|
||||||
|
assert exceptions_count < source2.timeperiodexception_set.count()
|
||||||
|
|
||||||
|
setting['EXCEPTIONS_SOURCES'] = {}
|
||||||
|
with override_settings(**setting):
|
||||||
|
call_command('sync_desks_timeperiod_exceptions_from_settings')
|
||||||
|
assert not TimePeriodExceptionSource.objects.exists()
|
||||||
|
|
||||||
|
|
||||||
def test_base_meeting_duration():
|
def test_base_meeting_duration():
|
||||||
agenda = Agenda(label='Meeting', kind='meetings')
|
agenda = Agenda(label='Meeting', kind='meetings')
|
||||||
agenda.save()
|
agenda.save()
|
||||||
|
|
|
@ -11,10 +11,12 @@ import os
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
from django.test import override_settings
|
||||||
from django.test.utils import CaptureQueriesContext
|
from django.test.utils import CaptureQueriesContext
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.timezone import make_aware, now, localtime
|
from django.utils.timezone import make_aware, now, localtime
|
||||||
|
|
||||||
|
import datetime
|
||||||
import freezegun
|
import freezegun
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
|
@ -2451,6 +2453,58 @@ END:VCALENDAR"""
|
||||||
assert exceptions[0].pk != new_exceptions[0].pk
|
assert exceptions[0].pk != new_exceptions[0].pk
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(
|
||||||
|
EXCEPTIONS_SOURCES={'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'},}
|
||||||
|
)
|
||||||
|
def test_meetings_agenda_time_period_exception_source_from_settings(app, admin_user):
|
||||||
|
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
|
||||||
|
desk = Desk.objects.create(agenda=agenda, label='Desk A')
|
||||||
|
MeetingType(agenda=agenda, label='Blah').save()
|
||||||
|
TimePeriod.objects.create(
|
||||||
|
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
|
||||||
|
)
|
||||||
|
assert TimePeriodException.objects.exists()
|
||||||
|
|
||||||
|
login(app)
|
||||||
|
resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
|
||||||
|
resp = resp.click('Settings')
|
||||||
|
resp = resp.click('upload')
|
||||||
|
assert 'Holidays' in resp.text
|
||||||
|
assert 'disabled' not in resp.text
|
||||||
|
assert 'refresh' not in resp.text
|
||||||
|
|
||||||
|
resp = resp.click('disable').follow()
|
||||||
|
assert not TimePeriodException.objects.exists()
|
||||||
|
|
||||||
|
resp = resp.click('upload')
|
||||||
|
assert 'Holidays' in resp.text
|
||||||
|
assert 'disabled' in resp.text
|
||||||
|
|
||||||
|
resp = resp.click('enable').follow()
|
||||||
|
assert TimePeriodException.objects.exists()
|
||||||
|
|
||||||
|
resp = resp.click('upload')
|
||||||
|
assert 'disabled' not in resp.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_meetings_agenda_time_period_exception_source_try_disable_ics(app, admin_user):
|
||||||
|
agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
|
||||||
|
desk = Desk.objects.create(agenda=agenda, label='Desk A')
|
||||||
|
MeetingType(agenda=agenda, label='Blah').save()
|
||||||
|
TimePeriod.objects.create(
|
||||||
|
weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
|
||||||
|
)
|
||||||
|
source = TimePeriodExceptionSource.objects.create(desk=desk, ics_url='https://example.com/test.ics')
|
||||||
|
|
||||||
|
login(app)
|
||||||
|
resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
|
||||||
|
resp = resp.click('Settings')
|
||||||
|
resp = resp.click('upload')
|
||||||
|
assert 'test.ics' in resp.text
|
||||||
|
|
||||||
|
assert app.get('/manage/time-period-exceptions-source/%s/toggle' % source.pk, status=404)
|
||||||
|
|
||||||
|
|
||||||
def test_agenda_day_view(app, admin_user, manager_user, api_user):
|
def test_agenda_day_view(app, admin_user, manager_user, api_user):
|
||||||
agenda = Agenda.objects.create(label='New Example', kind='meetings')
|
agenda = Agenda.objects.create(label='New Example', kind='meetings')
|
||||||
desk = Desk.objects.create(agenda=agenda, label='New Desk')
|
desk = Desk.objects.create(agenda=agenda, label='New Desk')
|
||||||
|
|
Loading…
Reference in New Issue