manager: add views for virtual agendas (#37123)

This commit is contained in:
Emmanuel Cazenave 2020-02-18 12:14:36 +01:00 committed by Frédéric Péters
parent 565d471d07
commit e2f041511f
9 changed files with 587 additions and 4 deletions

View File

@ -107,6 +107,9 @@ class Agenda(models.Model):
class Meta:
ordering = ['label']
def __str__(self):
return self.label
def save(self, *args, **kwargs):
if not self.slug:
self.slug = generate_slug(self)
@ -137,18 +140,22 @@ class Agenda(models.Model):
return self.real_agendas.all()
return [self]
def iter_meetingtypes(self):
def iter_meetingtypes(self, excluded_agenda=None):
""" Expose agenda's meetingtypes.
straighforward on a real agenda
On a virtual agenda we expose transient meeting types based on on the
the real ones shared by every real agendas.
"""
if self.kind == 'virtual':
base_qs = MeetingType.objects.filter(agenda__virtual_agendas__in=[self])
real_agendas = self.real_agendas
if excluded_agenda:
base_qs = base_qs.exclude(agenda=excluded_agenda)
real_agendas = real_agendas.exclude(pk=excluded_agenda.pk)
queryset = (
MeetingType.objects.filter(agenda__virtual_agendas__in=[self])
.values('slug', 'duration', 'label')
base_qs.values('slug', 'duration', 'label')
.annotate(total=Count('*'))
.filter(total=self.real_agendas.count())
.filter(total=real_agendas.count())
)
return [
MeetingType(duration=mt['duration'], label=mt['label'], slug=mt['slug'])
@ -176,6 +183,9 @@ class Agenda(models.Model):
return MeetingType.objects.get(id=id_, agenda=self)
return MeetingType.objects.get(slug=slug, agenda=self)
def get_virtual_members(self):
return VirtualMember.objects.filter(virtual_agenda=self)
def get_base_meeting_duration(self):
durations = [x.duration for x in self.iter_meetingtypes()]
if not durations:
@ -249,6 +259,46 @@ class VirtualMember(models.Model):
Agenda, on_delete=models.CASCADE, related_name='virtual_members', verbose_name='Agenda'
)
def clean(self):
error_msg = [_('This agenda does not have the same meeting types provided by the virtual agenda.')]
error = False
virtual_meetingtypes = self.virtual_agenda.iter_meetingtypes(excluded_agenda=self.real_agenda)
for meetingtype in virtual_meetingtypes:
try:
MeetingType.objects.get(
agenda=self.real_agenda,
label=meetingtype.label,
slug=meetingtype.slug,
duration=meetingtype.duration,
)
except MeetingType.DoesNotExist:
error = True
error_msg += [
_(
'Meeting type "%s" (%s minutes) (identifier: %s) does no exist.'
% (meetingtype.label, meetingtype.duration, meetingtype.slug)
)
]
if error:
raise ValidationError(error_msg)
num_virt_meetingtypes = len(virtual_meetingtypes)
if (
num_virt_meetingtypes
and num_virt_meetingtypes != MeetingType.objects.filter(agenda=self.real_agenda).count()
):
extra_qs = MeetingType.objects.filter(agenda=self.real_agenda)
for virt_meetingtype in virtual_meetingtypes:
extra_qs = extra_qs.exclude(
slug=virt_meetingtype.slug, label=virt_meetingtype.label, duration=meetingtype.duration
)
for extra_meeting_type in extra_qs:
error = True
error_msg += ['Extra meeting type, "%s".' % extra_meeting_type.label]
if error:
raise ValidationError(error_msg)
WEEKDAYS_LIST = sorted(WEEKDAYS.items(), key=lambda x: x[0])

View File

@ -34,6 +34,7 @@ from chrono.agendas.models import (
Desk,
TimePeriodException,
TimePeriodExceptionSource,
VirtualMember,
WEEKDAYS_LIST,
)
@ -88,6 +89,16 @@ class NewMeetingTypeForm(forms.ModelForm):
}
exclude = ['slug']
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.")
)
class MeetingTypeForm(forms.ModelForm):
class Meta:
@ -97,6 +108,21 @@ class MeetingTypeForm(forms.ModelForm):
}
exclude = []
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)
)
class TimePeriodAddForm(forms.Form):
weekdays = forms.MultipleChoiceField(
@ -161,6 +187,21 @@ class TimePeriodExceptionForm(forms.ModelForm):
return self.cleaned_data['end_datetime']
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']]
)
class ImportEventsForm(forms.Form):
events_csv_file = forms.FileField(
label=_('Events File'),

View File

@ -0,0 +1,19 @@
{% extends "chrono/manager_home.html" %}
{% load i18n %}
{% block appbar %}
<h2>{% trans "Exclude Agenda" %}</h2>
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
<p>
{% blocktrans %}Are you sure you want to exclude this agenda from the virtual agenda ?{% endblocktrans %}
</p>
<div class="buttons">
<button class="delete-button">{% trans 'Exclude' %}</button>
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' agenda.pk %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,63 @@
{% extends "chrono/manager_agenda_settings.html" %}
{% load i18n %}
{% block agenda-extra-management-actions %}
<a rel="popup" href="{% url 'chrono-manager-agenda-add-virtual-member' pk=object.id %}">{% trans 'Include Agenda' %}</a>
{% endblock %}
{% block agenda-settings %}
<div class="section">
<h3>{% trans 'Included Agendas' %}</h3>
<div>
{% if virtual_members %}
<ul class="objects-list single-links">
{% for virtual_member, can_be_managed in virtual_members %}
<li><a {% if can_be_managed %}href="{% url 'chrono-manager-agenda-settings' pk=virtual_member.real_agenda.id %}"{% endif %}>
{{virtual_member.real_agenda.label}}
</a>
<a rel="popup" class="delete" href="{% url 'chrono-manager-virtual-member-delete' pk=virtual_member.pk %}">{% trans "remove" %}</a>
</li>
{% endfor %}
</ul>
{% else %}
<div class="big-msg-info">
{% blocktrans %}
This virtual agenda doesn't include any agenda yet. Click on the "Include Agenda" button in
the top right of the page to include a first one.
{% endblocktrans %}
</div>
{% endif %}
</div>
</div>
{% if virtual_members %}
<div class="section">
<h3>{% trans 'Meeting Types' %}</h3>
<div>
{% if meeting_types %}
<ul class="objects-list single-links">
{% for meeting_type in meeting_types %}
<li><a rel="popup" href="">
{{meeting_type.label}}
<span class="duration">({{meeting_type.duration}} {% trans "minutes" %})</span>
<span class="identifier">[{% trans "identifier:" %} {{meeting_type.slug}}]</span>
</a>
</li>
{% endfor %}
</ul>
{% else %}
<div class="errornotice">
{% blocktrans %}
This virtual agenda doesn't have any meeting type.
It is probably because its included agendas have incompatible meeting types
and it makes this virtual agenda unusable.
{% endblocktrans %}
</div>
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "chrono/manager_agenda_view.html" %}
{% load i18n %}
{% block content %}
<div class="section">
<h3>{% trans 'Included Agendas' %}</h3>
<div>
{% if real_agendas %}
<ul class="objects-list single-links">
{% for real_agenda, can_be_viewed in real_agendas %}
<li><a {% if can_be_viewed %}href="{% url 'chrono-manager-agenda-view' pk=real_agenda.pk %}"{% endif %}>
{{real_agenda.label}}
</a>
{% endfor %}
</ul>
{% else %}
<div class="big-msg-info">
{% blocktrans %}
This virtual agenda is empty.
{% endblocktrans %}
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,28 @@
{% extends "chrono/manager_agenda_view.html" %}
{% load i18n %}
{% block extrascripts %}
{{ block.super }}
{{ form.media }}
{% endblock %}
{% block breadcrumb %}
{{ block.super }}
<a href="">{% trans "Include Agenda" %}</a>
{% endblock %}
{% block appbar %}
<h2>{% trans "Include Agenda" %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Save" %}</button>
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -93,6 +93,16 @@ urlpatterns = [
views.desk_import_time_period_exceptions,
name='chrono-manager-desk-add-import-time-period-exceptions',
),
url(
r'^agendas/(?P<pk>\d+)/add-virtual-member$',
views.agenda_add_virtual_member,
name='chrono-manager-agenda-add-virtual-member',
),
url(
r'^virtual-members/(?P<pk>\d+)/delete$',
views.virtual_member_delete,
name='chrono-manager-virtual-member-delete',
),
url(
r'^time-period-exceptions/(?P<pk>\d+)/edit$',
views.time_period_exception_edit,

View File

@ -23,6 +23,7 @@ from django.db.models import Q
from django.forms import ValidationError
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.urls import reverse, reverse_lazy
from django.utils.dates import MONTHS
from django.utils.timezone import now, make_aware, make_naive
@ -53,6 +54,7 @@ from chrono.agendas.models import (
ICSError,
AgendaImportError,
TimePeriodExceptionSource,
VirtualMember,
)
from .forms import (
@ -71,6 +73,7 @@ from .forms import (
AgendasImportForm,
TimePeriodAddForm,
TimePeriodExceptionSourceReplaceForm,
VirtualMemberForm,
)
from .utils import import_site
@ -241,6 +244,21 @@ agenda_delete = AgendaDeleteView.as_view()
class AgendaView(ViewableAgendaMixin, View):
def get(self, request, *args, **kwargs):
today = datetime.date.today()
if self.agenda.kind == 'virtual':
real_agendas = [
(agenda, agenda.can_be_viewed(request.user)) for agenda in self.agenda.real_agendas.all()
]
return TemplateResponse(
request=request,
template='chrono/manager_virtual_agenda_view.html',
context={
'real_agendas': real_agendas,
'agenda': self.agenda,
'object': self.agenda,
'user_can_manage': self.agenda.can_be_managed(self.request.user),
},
)
if self.agenda.kind == 'meetings':
# redirect to today view
return HttpResponseRedirect(
@ -665,6 +683,16 @@ class ManagedDeskSubobjectMixin(object):
class AgendaSettings(ManagedAgendaMixin, DetailView):
model = Agenda
def get_context_data(self, **kwargs):
context = super(AgendaSettings, self).get_context_data(**kwargs)
if self.agenda.kind == 'virtual':
context['virtual_members'] = [
(virtual_member, virtual_member.real_agenda.can_be_managed(self.request.user))
for virtual_member in self.object.get_virtual_members()
]
context['meeting_types'] = self.object.iter_meetingtypes()
return context
def get_events(self):
return Event.annotate_queryset(Event.objects.filter(agenda=self.agenda).select_related('agenda'))
@ -830,6 +858,35 @@ class MeetingTypeDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = MeetingType
def get_context_data(self, **kwargs):
context = super(MeetingTypeDeleteView, self).get_context_data(**kwargs)
cannot_delete = False
meeting_type = self.get_object()
for virtual_agenda in self.get_object().agenda.virtual_agendas.all():
if virtual_agenda.real_agendas.count() == 1:
continue
for mt in virtual_agenda.iter_meetingtypes():
if (
meeting_type.slug == mt.slug
and meeting_type.label == mt.label
and meeting_type.duration == mt.duration
):
cannot_delete = True
context['cannot_delete_msg'] = _(
'This cannot be removed as it used by a virtual agenda: %(agenda)s'
% {'agenda': virtual_agenda}
)
break
context['cannot_delete'] = cannot_delete
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(MeetingTypeDeleteView, self).delete(request, *args, **kwargs)
meeting_type_delete = MeetingTypeDeleteView.as_view()
@ -911,6 +968,42 @@ class DeskDeleteView(ManagedAgendaSubobjectMixin, DeleteView):
desk_delete = DeskDeleteView.as_view()
class VirtualMemberAddView(ManagedAgendaMixin, CreateView):
template_name = 'chrono/manager_virtual_member_form.html'
form_class = VirtualMemberForm
model = VirtualMember
def get_form_kwargs(self):
kwargs = super(VirtualMemberAddView, self).get_form_kwargs()
kwargs['initial']['virtual_agenda'] = kwargs['initial']['agenda']
return kwargs
agenda_add_virtual_member = VirtualMemberAddView.as_view()
class VirtualMemberDeleteView(DeleteView):
template_name = 'chrono/manager_confirm_virtual_member_delete.html'
model = VirtualMember
def dispatch(self, request, *args, **kwargs):
self.agenda = self.get_object().virtual_agenda
if not self.agenda.can_be_managed(request.user):
raise PermissionDenied()
return super(VirtualMemberDeleteView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(VirtualMemberDeleteView, self).get_context_data(**kwargs)
context['agenda'] = self.agenda
return context
def get_success_url(self):
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.pk})
virtual_member_delete = VirtualMemberDeleteView.as_view()
class AgendaAddTimePeriodExceptionView(ManagedDeskMixin, CreateView):
template_name = 'chrono/manager_time_period_exception_form.html'
model = TimePeriodException

View File

@ -25,6 +25,7 @@ from chrono.agendas.models import (
TimePeriod,
TimePeriodException,
TimePeriodExceptionSource,
VirtualMember,
)
pytestmark = pytest.mark.django_db
@ -2171,3 +2172,256 @@ def test_import_agenda(app, admin_user):
resp.form['agendas_json'] = Upload('export.json', agenda_export, 'application/json')
resp = resp.form.submit()
assert u'Missing &quot;gé1&quot; role' in resp.text
def test_virtual_agenda_add(app, admin_user):
app = login(app)
resp = app.get('/manage/', status=200)
resp = resp.click('New')
resp.form['label'] = 'Virtual agenda'
resp.form['kind'] = 'virtual'
resp = resp.form.submit()
agenda = Agenda.objects.get(label='Virtual agenda')
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id)
def test_virtual_agenda_baseview_empty(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
app = login(app)
resp = app.get(agenda.get_absolute_url())
assert 'Settings' in resp.text
assert 'My Virtual agenda' in resp.text
assert 'Included Agendas' in resp.text
assert 'This virtual agenda is empty.' in resp.text
assert '/manage/agendas/%s/settings' % agenda.pk in resp.text
def test_virtual_agenda_baseview(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
app = login(app)
resp = app.get(agenda.get_absolute_url())
assert 'Settings' in resp.text
assert 'My Virtual agenda' in resp.text
assert 'Included Agendas' in resp.text
assert 'This virtual agenda is empty.' not in resp.text
for real_agenda in [meeting_agenda_1, meeting_agenda_2]:
assert real_agenda.label in resp.text
assert real_agenda.get_absolute_url() in resp.text
def test_virtual_agenda_settings_empty(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert 'Include Agenda' in resp.text
assert 'Options' in resp.text
assert 'Export' in resp.text
assert 'Delete' in resp.text
assert 'Included Agendas' in resp.text
assert "This virtual agenda doesn't include any agenda yet" in resp.text
# No meeting types yet
assert 'Meeting Types' not in resp.text
def test_virtual_agenda_settings(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert "This virtual agenda doesn't include any agenda yet" not in resp.text
for real_agenda in [meeting_agenda_1, meeting_agenda_2]:
assert real_agenda.label in resp.text
assert '/manage/agendas/%s/settings' % real_agenda.pk in resp.text
assert 'Meeting Types' in resp.text
assert 'MT' in resp.text
assert 'mt' in resp.text
assert '10' in resp.text
# Error message when incompatible meeting types
mt2.delete()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
assert "This virtual agenda doesn't have any meeting type." in resp.text
def test_virtual_agenda_settings_include(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
Agenda.objects.create(label='Event agenda', kind='events')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
mt1 = MeetingType.objects.create(label='MT', duration=30, agenda=meeting_agenda_1)
Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Include Agenda')
# Only meetings agenda are proposed (2) + 1 empty choice = 3
assert len(resp.form['real_agenda'].options) == 3
# Include a real agenda
resp.form['real_agenda'].value = meeting_agenda_1.pk
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)
resp = resp.follow()
resp = resp.click('Include Agenda')
# The previously include agenda is not proposed any more
assert len(resp.form['real_agenda'].options) == 2
def test_virtual_agenda_settings_include_incompatible_agenda(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
app = login(app)
# refused because different slug
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mtt', duration=10)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Include Agenda')
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Meeting type &quot;MT&quot; (10 minutes) (identifier: mt) does no exist.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
# refused because different duration
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=15)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Include Agenda')
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Meeting type &quot;MT&quot; (10 minutes) (identifier: mt) does no exist.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
# refused because different label
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MTT', slug='mt', duration=10)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Include Agenda')
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
# refused because has one more meeting type
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='AA', slug='aa', duration=30)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Include Agenda')
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Extra meeting type, &quot;AA&quot;.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
mt2.delete()
# refused because has one less meeting type
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
mt2 = MeetingType.objects.create(agenda=meeting_agenda_1, label='AA', slug='aa', duration=30)
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
resp = resp.click('Include Agenda')
resp.form['real_agenda'].value = meeting_agenda_2.pk
resp = resp.form.submit()
assert 'This agenda does not have the same meeting types provided by the virtual agenda.' in resp.text
assert 'Meeting type &quot;AA&quot; (30 minutes) (identifier: aa) does no exist.' in resp.text
assert meeting_agenda_2.virtual_agendas.count() == 0
mt.delete()
mt2.delete()
def test_cant_delete_meetingtype_used_by_virtual_agenda(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
mt1 = MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
# ok because there is only one agenda in the virtual agenda
app = login(app)
resp = app.get('/manage/agendas/%s/settings' % meeting_agenda_1.pk)
resp = resp.click('MT')
resp = resp.click('Delete')
resp = resp.form.submit()
assert not meeting_agenda_1.iter_meetingtypes()
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
resp = app.get('/manage/agendas/%s/settings' % meeting_agenda_2.pk)
resp = resp.click('MT')
resp = resp.click('Delete')
assert 'This cannot be removed as it used by a virtual agenda' in resp.text
assert 'disabled' in resp.text
resp = app.post('/manage/meetingtypes/%s/delete' % mt2.pk, status=403)
def test_cant_modify_meetingtype_used_by_virtual_agenda(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
mt1 = MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
app = login(app)
# ok because there is only one agenda in the virtual agenda
resp = app.get('/manage/meetingtypes/%s/edit' % mt1.pk)
resp.form['label'].value = 'MTT'
resp = resp.form.submit()
assert MeetingType.objects.get(agenda=meeting_agenda_1, label='MTT', slug='mt', duration=10)
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MTT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
app = login(app)
resp = app.get('/manage/meetingtypes/%s/edit' % mt2.pk)
resp.form['label'].value = 'Oho'
resp = resp.form.submit()
assert 'This meetingtype is used by a virtual agenda' in resp.text
mt = MeetingType.objects.get(pk=mt2.pk)
assert mt.label == 'MTT'
def test_cant_add_meetingtype_if_virtual_agenda(app, admin_user):
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1)
app = login(app)
# ok because there is only one agenda in the virtual agenda
resp = app.get('/manage/agendas/%s/add-meeting-type' % meeting_agenda_1.pk)
resp.form['duration'].value = '12'
resp.form['label'].value = 'Oho'
resp = resp.form.submit()
assert MeetingType.objects.filter(agenda=meeting_agenda_1).count() == 2
MeetingType.objects.get(agenda=meeting_agenda_1, label='Oho').delete()
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings')
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2)
MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10)
app = login(app)
resp = app.get('/manage/agendas/%s/add-meeting-type' % meeting_agenda_1.pk)
resp.form['duration'].value = '12'
resp.form['label'].value = 'Oho'
resp = resp.form.submit()
assert 'Can&#39;t add a meetingtype to an agenda that is included in a virtual agenda.' in resp.text
assert MeetingType.objects.filter(agenda=meeting_agenda_1).count() == 1