manager: add edit/view permissions to agendas (#13201)

This commit is contained in:
Frédéric Péters 2016-09-18 17:17:33 +02:00
parent f466770c8b
commit 6fa14100e3
7 changed files with 328 additions and 103 deletions

View File

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0006_require_contenttypes_0002'),
('agendas', '0009_auto_20160911_1640'),
]
operations = [
migrations.AlterModelOptions(
name='event',
options={'ordering': ['agenda', 'start_datetime', 'label']},
),
migrations.AddField(
model_name='agenda',
name='edit_role',
field=models.ForeignKey(related_name='+', default=None, verbose_name='Edit Role', to='auth.Group', blank=True, null=True),
),
migrations.AddField(
model_name='agenda',
name='view_role',
field=models.ForeignKey(related_name='+', default=None, verbose_name='View Role', to='auth.Group', blank=True, null=True),
),
]

View File

@ -16,6 +16,7 @@
import datetime
from django.contrib.auth.models import Group
from django.core.urlresolvers import reverse
from django.db import models
from django.db import transaction
@ -40,6 +41,10 @@ class Agenda(models.Model):
_('Minimal booking delay (in days)'), default=1)
maximal_booking_delay = models.PositiveIntegerField(
_('Maximal booking delay (in days)'), default=56) # eight weeks
edit_role = models.ForeignKey(Group, blank=True, null=True, default=None,
related_name='+', verbose_name=_('Edit Role'))
view_role = models.ForeignKey(Group, blank=True, null=True, default=None,
related_name='+', verbose_name=_('View Role'))
class Meta:
ordering = ['label']
@ -62,6 +67,18 @@ class Agenda(models.Model):
def get_absolute_url(self):
return reverse('chrono-manager-agenda-view', kwargs={'pk': self.id})
def can_be_managed(self, user):
if user.is_staff:
return True
group_ids = [x.id for x in user.groups.all()]
return bool(self.edit_role_id in group_ids)
def can_be_viewed(self, user):
if self.can_be_managed(user):
return True
group_ids = [x.id for x in user.groups.all()]
return bool(self.view_role_id in group_ids)
WEEKDAYS_LIST = sorted(WEEKDAYS.items(), key=lambda x: x[0])

View File

@ -5,14 +5,18 @@
<h2>{{ object.label }}
<span class="identifier">[{% trans "identifier:" %} {{object.id}}]</span></h2>
</h2>
<a rel="popup" href="{% url 'chrono-manager-agenda-delete' pk=object.id %}">{% trans 'Delete' %}</a>
<a rel="popup" href="{% url 'chrono-manager-agenda-edit' pk=object.id %}">{% trans 'Options' %}</a>
{% if object.kind == "events" %}
<a rel="popup" href="{% url 'chrono-manager-agenda-import-events' pk=object.id %}">{% trans 'Import Events' %}</a>
<a rel="popup" href="{% url 'chrono-manager-agenda-add-event' pk=object.id %}">{% trans 'New Event' %}</a>
{% else %}
<a rel="popup" href="{% url 'chrono-manager-agenda-add-meeting-type' pk=object.id %}">{% trans 'New Meeting Type' %}</a>
<a rel="popup" href="{% url 'chrono-manager-agenda-add-time-period' pk=object.id %}">{% trans 'New Time Period' %}</a>
{% if user.is_staff %}
<a rel="popup" href="{% url 'chrono-manager-agenda-delete' pk=object.id %}">{% trans 'Delete' %}</a>
{% endif %}
{% if user_can_manage %}
<a rel="popup" href="{% url 'chrono-manager-agenda-edit' pk=object.id %}">{% trans 'Options' %}</a>
{% if object.kind == "events" %}
<a rel="popup" href="{% url 'chrono-manager-agenda-import-events' pk=object.id %}">{% trans 'Import Events' %}</a>
<a rel="popup" href="{% url 'chrono-manager-agenda-add-event' pk=object.id %}">{% trans 'New Event' %}</a>
{% else %}
<a rel="popup" href="{% url 'chrono-manager-agenda-add-meeting-type' pk=object.id %}">{% trans 'New Meeting Type' %}</a>
<a rel="popup" href="{% url 'chrono-manager-agenda-add-time-period' pk=object.id %}">{% trans 'New Time Period' %}</a>
{% endif %}
{% endif %}
{% endblock %}
@ -39,7 +43,7 @@
{% elif event.waiting_list_places %}
data-total="{{event.waiting_list_places}}" data-booked="{{event.waiting_list}}"
{% endif %}
><a href="{% url 'chrono-manager-event-edit' pk=event.id %}">
><a href="{% if user_can_manage %}{% url 'chrono-manager-event-edit' pk=event.id %}{% else %}#{% endif %}">
{% if event.label %}{{event.label}} / {% endif %}
{{ event.start_datetime }}
{% if event.full %}/ <span class="full">{% trans "full" %}</span>{% endif %}
@ -77,7 +81,7 @@
<div>
<ul class="objects-list single-links">
{% for meeting_type in object.meetingtype_set.all %}
<li><a href="{% url 'chrono-manager-meeting-type-edit' pk=meeting_type.id %}">
<li><a href="{% if user_can_manage %}{% url 'chrono-manager-meeting-type-edit' pk=meeting_type.id %}{% else %}#{% endif %}">
{{meeting_type.label}}
<span class="identifier">[{% trans "identifier:" %} {{meeting_type.id}}]</span></h2>
</a>
@ -99,7 +103,7 @@
<div>
<ul class="objects-list single-links">
{% for time_period in object.timeperiod_set.all %}
<li><a href="{% url 'chrono-manager-time-period-edit' pk=time_period.id %}">
<li><a href="{% if user_can_manage %}{% url 'chrono-manager-time-period-edit' pk=time_period.id %}{% else %}#{% endif %}">
{{time_period.weekday_str}} / {{time_period.start_time}} → {{time_period.end_time}}
</a>
</li>

View File

@ -3,7 +3,9 @@
{% block appbar %}
<h2>{% trans 'Agendas' %}</h2>
{% if user.is_staff %}
<a rel="popup" href="{% url 'chrono-manager-agenda-add' %}">{% trans 'New' %}</a>
{% endif %}
{% endblock %}
{% block content %}

View File

@ -17,9 +17,10 @@
import json
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse, reverse_lazy
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.db.models import Q
from django.http import HttpResponse, Http404
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import force_text
from django.views.generic import (DetailView, CreateView, UpdateView,
@ -34,13 +35,25 @@ 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
fields = ['label', 'kind']
fields = ['label', 'kind', 'edit_role', 'view_role']
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
raise PermissionDenied()
return super(AgendaAddView, self).dispatch(request, *args, **kwargs)
agenda_add = AgendaAddView.as_view()
@ -48,7 +61,13 @@ agenda_add = AgendaAddView.as_view()
class AgendaEditView(UpdateView):
template_name = 'chrono/manager_agenda_form.html'
model = Agenda
fields = ['label', 'minimal_booking_delay', 'maximal_booking_delay']
fields = ['label', 'edit_role', 'view_role', 'minimal_booking_delay', 'maximal_booking_delay']
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
agenda_edit = AgendaEditView.as_view()
@ -58,6 +77,11 @@ class AgendaDeleteView(DeleteView):
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)
agenda_delete = AgendaDeleteView.as_view()
@ -65,27 +89,65 @@ class AgendaView(DetailView):
template_name = 'chrono/manager_agenda_view.html'
model = Agenda
def get_context_data(self, **kwargs):
context = super(AgendaView, self).get_context_data(**kwargs)
if not self.get_object().can_be_viewed(self.request.user):
raise PermissionDenied()
context['user_can_manage'] = self.get_object().can_be_managed(self.request.user)
return context
agenda_view = AgendaView.as_view()
class AgendaAddEventView(CreateView):
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 self.agenda.get_absolute_url()
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 self.agenda.get_absolute_url()
class AgendaAddEventView(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_event_form.html'
model = Event
form_class = EventForm
def get_success_url(self):
return reverse('chrono-manager-agenda-view', kwargs={'pk': self.kwargs.get('pk')})
def get_initial(self):
initial = super(AgendaAddEventView, self).get_initial()
initial['agenda'] = self.kwargs.get('pk')
return initial
def get_context_data(self, **kwargs):
context = super(AgendaAddEventView, self).get_context_data(**kwargs)
context['agenda'] = Agenda.objects.get(id=self.kwargs.get('pk'))
return context
agenda_add_event = AgendaAddEventView.as_view()
@ -95,130 +157,66 @@ class AgendaImportEventsSampleView(TemplateView):
agenda_import_events_sample_csv = AgendaImportEventsSampleView.as_view()
class AgendaImportEventsView(FormView):
class AgendaImportEventsView(ManagedAgendaMixin, FormView):
form_class = ImportEventsForm
template_name = 'chrono/manager_import_events.html'
agenda = None
def dispatch(self, request, *args, **kwargs):
self.agenda = get_object_or_404(Agenda, id=kwargs.get('pk'))
return super(AgendaImportEventsView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(AgendaImportEventsView, self).get_context_data(**kwargs)
context['agenda'] = self.agenda
return context
def form_valid(self, form):
if form.events:
for event in form.events:
event.agenda = self.agenda
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)
def get_success_url(self):
return reverse('chrono-manager-agenda-view', kwargs={'pk': self.agenda.id})
agenda_import_events = AgendaImportEventsView.as_view()
class EventEditView(UpdateView):
class EventEditView(ManagedAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_event_form.html'
model = Event
form_class = EventForm
def get_context_data(self, **kwargs):
context = super(EventEditView, self).get_context_data(**kwargs)
context['agenda'] = self.object.agenda
return context
def get_success_url(self):
return reverse('chrono-manager-agenda-view', kwargs={'pk': self.object.agenda_id})
event_edit = EventEditView.as_view()
class EventDeleteView(DeleteView):
class EventDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = Event
def get_success_url(self):
return self.object.agenda.get_absolute_url()
event_delete = EventDeleteView.as_view()
class AgendaAddMeetingTypeView(CreateView):
class AgendaAddMeetingTypeView(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_meeting_type_form.html'
model = Event
form_class = MeetingTypeForm
def get_success_url(self):
return reverse('chrono-manager-agenda-view', kwargs={'pk': self.kwargs.get('pk')})
def get_initial(self):
initial = super(AgendaAddMeetingTypeView, self).get_initial()
initial['agenda'] = self.kwargs.get('pk')
return initial
def get_context_data(self, **kwargs):
context = super(AgendaAddMeetingTypeView, self).get_context_data(**kwargs)
context['agenda'] = Agenda.objects.get(id=self.kwargs.get('pk'))
return context
agenda_add_meeting_type = AgendaAddMeetingTypeView.as_view()
class MeetingTypeEditView(UpdateView):
class MeetingTypeEditView(ManagedAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_meeting_type_form.html'
model = MeetingType
form_class = MeetingTypeForm
def get_context_data(self, **kwargs):
context = super(MeetingTypeEditView, self).get_context_data(**kwargs)
context['agenda'] = self.object.agenda
return context
def get_success_url(self):
return reverse('chrono-manager-agenda-view', kwargs={'pk': self.object.agenda_id})
meeting_type_edit = MeetingTypeEditView.as_view()
class AgendaAddTimePeriodView(CreateView):
class AgendaAddTimePeriodView(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_time_period_form.html'
model = Event
form_class = TimePeriodForm
def get_success_url(self):
return reverse('chrono-manager-agenda-view', kwargs={'pk': self.kwargs.get('pk')})
def get_initial(self):
initial = super(AgendaAddTimePeriodView, self).get_initial()
initial['agenda'] = self.kwargs.get('pk')
return initial
def get_context_data(self, **kwargs):
context = super(AgendaAddTimePeriodView, self).get_context_data(**kwargs)
context['agenda'] = Agenda.objects.get(id=self.kwargs.get('pk'))
return context
agenda_add_time_period = AgendaAddTimePeriodView.as_view()
class TimePeriodEditView(UpdateView):
class TimePeriodEditView(ManagedAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_time_period_form.html'
model = TimePeriod
form_class = TimePeriodForm
def get_context_data(self, **kwargs):
context = super(TimePeriodEditView, self).get_context_data(**kwargs)
context['agenda'] = self.object.agenda
return context
def get_success_url(self):
return reverse('chrono-manager-agenda-view', kwargs={'pk': self.object.agenda_id})
time_period_edit = TimePeriodEditView.as_view()

View File

@ -19,6 +19,10 @@
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
from django.db.models import Q
from .agendas.models import Agenda
class DecoratedURLPattern(RegexURLPattern):
def resolve(self, *args, **kwargs):
@ -53,6 +57,10 @@ def manager_required(function=None, login_url=None):
if user and user.is_staff:
return True
if user and not user.is_anonymous():
# /manage/ is open to anyone authorized to view or edit an agenda.
group_ids = [x.id for x in user.groups.all()]
if Agenda.objects.filter(Q(view_role_id__in=group_ids) | Q(edit_role_id__in=group_ids)).exists():
return True
raise PermissionDenied()
# As the last resort, show the login form
return False

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.models import User
from django.contrib.auth.models import User, Group
from django.utils.timezone import make_aware
import datetime
import pytest
@ -20,6 +20,18 @@ def simple_user():
user = User.objects.create_user('user', password='user')
return user
@pytest.fixture
def manager_user():
try:
user = User.objects.get(username='manager')
except User.DoesNotExist:
user = User.objects.create_user('manager', password='manager')
group, created = Group.objects.get_or_create(name='Managers')
if created:
group.save()
user.groups = [group]
return user
@pytest.fixture
def admin_user():
try:
@ -46,6 +58,26 @@ def test_simple_user_access(app, simple_user):
app = login(app, username='user', password='user')
assert app.get('/manage/', status=403)
def test_manager_user_access(app, manager_user):
# connect while being logged as a manager user, access should be granted if
# there's at least an agenda that is viewable or editable.
app = login(app, username='manager', password='manager')
assert app.get('/manage/', status=403)
agenda = Agenda(label=u'Foo bar')
agenda.save()
assert app.get('/manage/', status=403)
agenda.view_role = manager_user.groups.all()[0]
agenda.edit_role = None
agenda.save()
assert app.get('/manage/', status=200)
agenda.edit_role = manager_user.groups.all()[0]
agenda.view_role = None
agenda.save()
assert app.get('/manage/', status=200)
def test_home_redirect(app):
assert app.get('/', status=302).location == 'http://localhost:80/manage/'
@ -73,17 +105,44 @@ def test_menu_json(app, admin_user):
resp2 = app.get('/manage/menu.json?callback=Q', status=200)
assert resp2.body == 'Q(%s);' % resp.body
def test_view_agendas_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo Bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
agenda = Agenda(label=u'Bar Foo')
agenda.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/', status=200)
assert 'Foo Bar' in resp.body
assert 'Bar Foo' not in resp.body
assert 'New' not in resp.body
app.get('/manage/agendas/%s/' % agenda.id, status=403)
def test_add_agenda(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
resp = resp.click('New')
resp.form['label'] = 'Foo bar'
resp = resp.form.submit()
agenda = Agenda.objects.get(label='Foo bar')
assert resp.location == 'http://localhost:80/manage/agendas/%s/' % agenda.id
resp = resp.follow()
assert '<h2>Foo bar' in resp.body
def test_add_agenda_as_manager(app, manager_user):
# open /manage/ access to manager_user, and check agenda creation is not
# allowed.
agenda = Agenda(label='Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/', status=200)
resp = app.get('/manage/agendas/add/', status=403)
def test_options_agenda(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
@ -98,6 +157,29 @@ def test_options_agenda(app, admin_user):
resp = resp.follow()
assert '<h2>Foo baz' in resp.body
def test_options_agenda_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/', status=200)
resp = resp.click('Foo bar')
assert not 'Options' in resp.body
resp = app.get('/manage/agendas/%s/edit' % agenda.id, status=403)
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/', status=200)
resp = resp.click('Foo bar')
resp = resp.click('Options')
assert resp.form['label'].value == 'Foo bar'
resp.form['label'] = 'Foo baz'
resp = resp.form.submit()
assert resp.location == 'http://localhost:80/manage/agendas/%s/' % agenda.id
resp = resp.follow()
assert '<h2>Foo baz' in resp.body
def test_delete_agenda(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
@ -110,6 +192,17 @@ def test_delete_agenda(app, admin_user):
resp = resp.follow()
assert not 'Foo bar' in resp.body
def test_delete_agenda_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/', status=200)
resp = resp.click('Foo bar')
assert 'Options' in resp.body
assert 'Delete' not in resp.body
resp = app.get('/manage/agendas/%s/delete' % agenda.id, status=403)
def test_add_event(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
@ -127,6 +220,35 @@ def test_add_event(app, admin_user):
assert 'Feb. 15, 2016, 5 p.m.' in resp.body
assert '10 places' in resp.body
def test_add_event_on_missing_agenda(app, admin_user):
app = login(app)
app.get('/manage/agendas/%s/add-event' % '999', status=404)
def test_add_event_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/' % agenda.id, status=200)
assert "This agenda doesn't have any event yet." in resp.body
assert '>New Event<' not in resp.body
app.get('/manage/agendas/%s/add-event' % agenda.id, status=403)
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/' % agenda.id, status=200)
resp = resp.click('New Event')
resp.form['start_datetime'] = '2016-02-15 17:00'
resp.form['places'] = 10
resp = resp.form.submit()
resp = resp.follow()
event = Event.objects.get(places=10)
assert not "This agenda doesn't have any event yet." in resp.body
assert '/manage/events/%s/' % event.id in resp.body
assert 'Feb. 15, 2016, 5 p.m.' in resp.body
assert '10 places' in resp.body
def test_edit_event(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
@ -145,6 +267,35 @@ def test_edit_event(app, admin_user):
assert 'Feb. 16, 2016, 5 p.m.' in resp.body
assert '20 places' in resp.body
def test_edit_missing_event(app, admin_user):
app = login(app)
app.get('/manage/agendas/999/', status=404)
def test_edit_event_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
event = Event(start_datetime=datetime.datetime(2016, 2, 15, 17, 0),
places=20, agenda=agenda)
event.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/' % agenda.id, status=200)
assert '/events/%s/' % event.id not in resp.body
resp = app.get('/manage/events/%s/' % event.id, status=403)
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/' % agenda.id, status=200)
resp = resp.click('Feb. 15, 2016, 5 p.m.')
assert resp.form['start_datetime'].value == '15/02/2016 17:00'
resp.form['start_datetime'] = '2016-02-16 17:00'
resp.form['places'] = 20
resp = resp.form.submit()
resp = resp.follow()
assert '/manage/events/%s/' % event.id in resp.body
assert 'Feb. 16, 2016, 5 p.m.' in resp.body
assert '20 places' in resp.body
def test_booked_places(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()
@ -199,6 +350,22 @@ def test_delete_event(app, admin_user):
assert resp.location == 'http://localhost:80/manage/agendas/%s/' % agenda.id
assert Event.objects.count() == 0
def test_delete_event_as_manager(app, manager_user):
agenda = Agenda(label=u'Foo bar')
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
event = Event(start_datetime=datetime.datetime(2016, 2, 15, 17, 0),
places=10, agenda=agenda)
event.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/' % agenda.id, status=200)
resp = resp.click(href='/manage/events/%s' % event.id)
resp = resp.click('Delete')
resp = resp.form.submit()
assert resp.location == 'http://localhost:80/manage/agendas/%s/' % agenda.id
assert Event.objects.count() == 0
def test_import_events(app, admin_user):
agenda = Agenda(label=u'Foo bar')
agenda.save()