manager: add views for virtual agendas (#37123)
This commit is contained in:
parent
565d471d07
commit
e2f041511f
|
@ -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])
|
||||
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 "gé1" 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 "MT" (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 "MT" (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, "AA".' 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 "AA" (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'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
|
||||
|
|
Loading…
Reference in New Issue