0002-manager-add-views-for-virtual-agendas-37123.patch
chrono/agendas/models.py | ||
---|---|---|
107 | 107 |
class Meta: |
108 | 108 |
ordering = ['label'] |
109 | 109 | |
110 |
def __str__(self): |
|
111 |
return self.label |
|
112 | ||
110 | 113 |
def save(self, *args, **kwargs): |
111 | 114 |
if not self.slug: |
112 | 115 |
self.slug = generate_slug(self) |
... | ... | |
176 | 179 |
return MeetingType.objects.get(id=id_, agenda=self) |
177 | 180 |
return MeetingType.objects.get(slug=slug, agenda=self) |
178 | 181 | |
182 |
def get_virtual_members(self): |
|
183 |
return VirtualMember.objects.filter(virtual_agenda=self) |
|
184 | ||
179 | 185 |
def get_base_meeting_duration(self): |
180 | 186 |
durations = [x.duration for x in self.iter_meetingtypes()] |
181 | 187 |
if not durations: |
... | ... | |
245 | 251 | |
246 | 252 |
class VirtualMember(models.Model): |
247 | 253 |
virtual_agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE, related_name='real_members') |
248 |
real_agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE, related_name='virtual_members') |
|
254 |
real_agenda = models.ForeignKey( |
|
255 |
Agenda, on_delete=models.CASCADE, related_name='virtual_members', verbose_name='Agenda' |
|
256 |
) |
|
257 | ||
258 |
def clean(self): |
|
259 |
for meetingtype in self.virtual_agenda.iter_meetingtypes(): |
|
260 |
try: |
|
261 |
MeetingType.objects.get( |
|
262 |
agenda=self.real_agenda, |
|
263 |
label=meetingtype.label, |
|
264 |
slug=meetingtype.slug, |
|
265 |
duration=meetingtype.duration, |
|
266 |
) |
|
267 |
except MeetingType.DoesNotExist: |
|
268 |
raise ValidationError( |
|
269 |
_('This agenda does not the meetingtypes provided by the virtualagenda.') |
|
270 |
) |
|
249 | 271 | |
250 | 272 | |
251 | 273 |
WEEKDAYS_LIST = sorted(WEEKDAYS.items(), key=lambda x: x[0]) |
chrono/manager/forms.py | ||
---|---|---|
34 | 34 |
Desk, |
35 | 35 |
TimePeriodException, |
36 | 36 |
TimePeriodExceptionSource, |
37 |
VirtualMember, |
|
37 | 38 |
WEEKDAYS_LIST, |
38 | 39 |
) |
39 | 40 | |
... | ... | |
161 | 162 |
return self.cleaned_data['end_datetime'] |
162 | 163 | |
163 | 164 | |
165 |
class VirtualMemberForm(forms.ModelForm): |
|
166 |
class Meta: |
|
167 |
model = VirtualMember |
|
168 |
fields = ['virtual_agenda', 'real_agenda'] |
|
169 |
widgets = { |
|
170 |
'virtual_agenda': forms.HiddenInput(), |
|
171 |
} |
|
172 | ||
173 |
def __init__(self, *args, **kwargs): |
|
174 |
super(VirtualMemberForm, self).__init__(*args, **kwargs) |
|
175 |
self.fields['real_agenda'].queryset = Agenda.objects.filter(kind='meetings').exclude( |
|
176 |
virtual_agendas__pk__in=[kwargs['initial']['agenda']] |
|
177 |
) |
|
178 | ||
179 | ||
164 | 180 |
class ImportEventsForm(forms.Form): |
165 | 181 |
events_csv_file = forms.FileField( |
166 | 182 |
label=_('Events File'), |
chrono/manager/templates/chrono/manager_confirm_virtual_member_delete.html | ||
---|---|---|
1 |
{% extends "chrono/manager_home.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block appbar %} |
|
5 |
<h2>{% trans "Exclude Agenda" %}</h2> |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% block content %} |
|
9 |
<form method="post"> |
|
10 |
{% csrf_token %} |
|
11 |
<p> |
|
12 |
{% blocktrans %}Are you sure you want to exclude this agenda from the virtual agenda ?{% endblocktrans %} |
|
13 |
</p> |
|
14 |
<div class="buttons"> |
|
15 |
<button class="delete-button">{% trans 'Exclude' %}</button> |
|
16 |
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' agenda.pk %}">{% trans 'Cancel' %}</a> |
|
17 |
</div> |
|
18 |
</form> |
|
19 |
{% endblock %} |
chrono/manager/templates/chrono/manager_virtual_agenda_settings.html | ||
---|---|---|
1 |
{% extends "chrono/manager_agenda_settings.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block agenda-extra-management-actions %} |
|
5 |
<a rel="popup" href="{% url 'chrono-manager-agenda-add-virtual-member' pk=object.id %}">{% trans 'Include Agenda' %}</a> |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% block agenda-settings %} |
|
9 | ||
10 |
<div class="section"> |
|
11 |
<h3>{% trans 'Included Agendas' %}</h3> |
|
12 |
<div> |
|
13 |
{% if virtual_members.count %} |
|
14 |
<ul class="objects-list single-links"> |
|
15 |
{% for virtual_member in virtual_members %} |
|
16 |
<li><a href="{% url 'chrono-manager-agenda-settings' pk=virtual_member.real_agenda.id %}"> |
|
17 |
{{real_agenda.label}} |
|
18 |
<span class="identifier">[{% trans "identifier:" %} {{virtual_member.real_agenda.slug}}]</span> |
|
19 |
</a> |
|
20 |
<a rel="popup" class="delete" href="{% url 'chrono-manager-virtual-member-delete' pk=virtual_member.pk %}">{% trans "remove" %}</a> |
|
21 |
</li> |
|
22 |
{% endfor %} |
|
23 |
</ul> |
|
24 |
{% else %} |
|
25 |
<div class="big-msg-info"> |
|
26 |
{% blocktrans %} |
|
27 |
This virtual agenda doesn't include any agenda yet. Click on the "Include Agenda" button in |
|
28 |
the top right of the page to include a first one. |
|
29 |
{% endblocktrans %} |
|
30 |
</div> |
|
31 |
{% endif %} |
|
32 |
</div> |
|
33 |
</div> |
|
34 | ||
35 |
{% if virtual_members.count %} |
|
36 |
<div class="section"> |
|
37 |
<h3>{% trans 'Meeting Types' %}</h3> |
|
38 |
<div> |
|
39 |
{% if meeting_types %} |
|
40 |
<ul class="objects-list single-links"> |
|
41 |
{% for meeting_type in meeting_types %} |
|
42 |
<li><a rel="popup" href=""> |
|
43 |
{{meeting_type.label}} |
|
44 |
<span class="duration">({{meeting_type.duration}} {% trans "minutes" %})</span> |
|
45 |
<span class="identifier">[{% trans "identifier:" %} {{meeting_type.slug}}]</span> |
|
46 |
</a> |
|
47 |
</li> |
|
48 |
{% endfor %} |
|
49 |
</ul> |
|
50 |
{% else %} |
|
51 |
<div class="errornotice"> |
|
52 |
{% blocktrans %} |
|
53 |
This virtual agenda doesn't have any meeting type. |
|
54 |
It is probably because its included agendas have incompatible meeting types |
|
55 |
and it makes this virtual agenda unusable. |
|
56 |
{% endblocktrans %} |
|
57 |
</div> |
|
58 |
{% endif %} |
|
59 |
</div> |
|
60 |
</div> |
|
61 |
{% endif %} |
|
62 | ||
63 | ||
64 |
{% endblock %} |
chrono/manager/templates/chrono/manager_virtual_agenda_view.html | ||
---|---|---|
1 |
{% extends "chrono/manager_agenda_view.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block content %} |
|
5 |
<div class="section"> |
|
6 |
<h3>{% trans 'Included Agendas' %}</h3> |
|
7 |
<div> |
|
8 |
{% if agenda.real_agendas.count %} |
|
9 |
<ul class="objects-list single-links"> |
|
10 |
{% for real_agenda in agenda.real_agendas.all %} |
|
11 |
<li><a href="{% url 'chrono-manager-agenda-view' pk=real_agenda.pk %}"> |
|
12 |
{{real_agenda.label}} |
|
13 |
<span class="identifier">[{% trans "identifier:" %} {{real_agenda.slug}}]</span> |
|
14 |
</a> |
|
15 |
{% endfor %} |
|
16 |
</ul> |
|
17 |
{% else %} |
|
18 |
<div class="big-msg-info"> |
|
19 |
{% blocktrans %} |
|
20 |
This virtual agenda is empty. |
|
21 |
{% endblocktrans %} |
|
22 |
</div> |
|
23 |
{% endif %} |
|
24 |
</div> |
|
25 |
</div> |
|
26 |
{% endblock %} |
chrono/manager/templates/chrono/manager_virtual_member_form.html | ||
---|---|---|
1 |
{% extends "chrono/manager_agenda_view.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block extrascripts %} |
|
5 |
{{ block.super }} |
|
6 |
{{ form.media }} |
|
7 |
{% endblock %} |
|
8 | ||
9 |
{% block breadcrumb %} |
|
10 |
{{ block.super }} |
|
11 |
<a href="">{% trans "Include Agenda" %}</a> |
|
12 |
{% endblock %} |
|
13 | ||
14 |
{% block appbar %} |
|
15 |
<h2>{% trans "Include Agenda" %}</h2> |
|
16 |
{% endblock %} |
|
17 | ||
18 |
{% block content %} |
|
19 | ||
20 |
<form method="post" enctype="multipart/form-data"> |
|
21 |
{% csrf_token %} |
|
22 |
{{ form.as_p }} |
|
23 |
<div class="buttons"> |
|
24 |
<button class="submit-button">{% trans "Save" %}</button> |
|
25 |
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Cancel' %}</a> |
|
26 |
</div> |
|
27 |
</form> |
|
28 |
{% endblock %} |
chrono/manager/urls.py | ||
---|---|---|
93 | 93 |
views.desk_import_time_period_exceptions, |
94 | 94 |
name='chrono-manager-desk-add-import-time-period-exceptions', |
95 | 95 |
), |
96 |
url( |
|
97 |
r'^agendas/(?P<pk>\d+)/add-virtual-member$', |
|
98 |
views.agenda_add_virtual_member, |
|
99 |
name='chrono-manager-agenda-add-virtual-member', |
|
100 |
), |
|
101 |
url( |
|
102 |
r'^virtual-members/(?P<pk>\d+)/delete$', |
|
103 |
views.virtual_member_delete, |
|
104 |
name='chrono-manager-virtual-member-delete', |
|
105 |
), |
|
96 | 106 |
url( |
97 | 107 |
r'^time-period-exceptions/(?P<pk>\d+)/edit$', |
98 | 108 |
views.time_period_exception_edit, |
chrono/manager/views.py | ||
---|---|---|
23 | 23 |
from django.forms import ValidationError |
24 | 24 |
from django.http import Http404, HttpResponse, HttpResponseRedirect |
25 | 25 |
from django.shortcuts import get_object_or_404 |
26 |
from django.template.response import TemplateResponse |
|
26 | 27 |
from django.urls import reverse, reverse_lazy |
27 | 28 |
from django.utils.dates import MONTHS |
28 | 29 |
from django.utils.timezone import now, make_aware, make_naive |
... | ... | |
53 | 54 |
ICSError, |
54 | 55 |
AgendaImportError, |
55 | 56 |
TimePeriodExceptionSource, |
57 |
VirtualMember, |
|
56 | 58 |
) |
57 | 59 | |
58 | 60 |
from .forms import ( |
... | ... | |
71 | 73 |
AgendasImportForm, |
72 | 74 |
TimePeriodAddForm, |
73 | 75 |
TimePeriodExceptionSourceReplaceForm, |
76 |
VirtualMemberForm, |
|
74 | 77 |
) |
75 | 78 |
from .utils import import_site |
76 | 79 | |
... | ... | |
241 | 244 |
class AgendaView(ViewableAgendaMixin, View): |
242 | 245 |
def get(self, request, *args, **kwargs): |
243 | 246 |
today = datetime.date.today() |
247 |
if self.agenda.kind == 'virtual': |
|
248 |
return TemplateResponse( |
|
249 |
request=request, |
|
250 |
template='chrono/manager_virtual_agenda_view.html', |
|
251 |
context={ |
|
252 |
'agenda': self.agenda, |
|
253 |
'object': self.agenda, |
|
254 |
'user_can_manage': self.agenda.can_be_managed(self.request.user), |
|
255 |
}, |
|
256 |
) |
|
257 | ||
244 | 258 |
if self.agenda.kind == 'meetings': |
245 | 259 |
# redirect to today view |
246 | 260 |
return HttpResponseRedirect( |
... | ... | |
633 | 647 |
class AgendaSettings(ManagedAgendaMixin, DetailView): |
634 | 648 |
model = Agenda |
635 | 649 | |
650 |
def get_context_data(self, **kwargs): |
|
651 |
context = super(AgendaSettings, self).get_context_data(**kwargs) |
|
652 |
if self.agenda.kind == 'virtual': |
|
653 |
context['virtual_members'] = self.object.get_virtual_members() |
|
654 |
context['meeting_types'] = self.object.iter_meetingtypes() |
|
655 |
return context |
|
656 | ||
636 | 657 |
def get_events(self): |
637 | 658 |
return Event.annotate_queryset(Event.objects.filter(agenda=self.agenda).select_related('agenda')) |
638 | 659 | |
... | ... | |
794 | 815 |
template_name = 'chrono/manager_confirm_delete.html' |
795 | 816 |
model = MeetingType |
796 | 817 | |
818 |
def get_context_data(self, **kwargs): |
|
819 |
context = super(MeetingTypeDeleteView, self).get_context_data(**kwargs) |
|
820 |
cannot_delete = False |
|
821 |
meeting_type = self.get_object() |
|
822 |
for virtual_agenda in self.get_object().agenda.virtual_agendas.all(): |
|
823 |
for mt in virtual_agenda.iter_meetingtypes(): |
|
824 |
if ( |
|
825 |
meeting_type.slug == mt.slug |
|
826 |
and meeting_type.label == mt.label |
|
827 |
and meeting_type.duration == mt.duration |
|
828 |
): |
|
829 |
cannot_delete = True |
|
830 |
context['cannot_delete_msg'] = _( |
|
831 |
'This cannot be removed as it used by a virtual agenda: %(agenda)s' |
|
832 |
% {'agenda': virtual_agenda} |
|
833 |
) |
|
834 |
break |
|
835 |
context['cannot_delete'] = cannot_delete |
|
836 |
return context |
|
837 | ||
838 |
def delete(self, request, *args, **kwargs): |
|
839 |
self.object = self.get_object() |
|
840 |
context = self.get_context_data() |
|
841 |
if context['cannot_delete']: |
|
842 |
raise PermissionDenied() |
|
843 |
return super(MeetingTypeDeleteView, self).delete(request, *args, **kwargs) |
|
844 | ||
797 | 845 | |
798 | 846 |
meeting_type_delete = MeetingTypeDeleteView.as_view() |
799 | 847 | |
... | ... | |
875 | 923 |
desk_delete = DeskDeleteView.as_view() |
876 | 924 | |
877 | 925 | |
926 |
class VirtualMemberAddView(ManagedAgendaMixin, CreateView): |
|
927 |
template_name = 'chrono/manager_virtual_member_form.html' |
|
928 |
form_class = VirtualMemberForm |
|
929 |
model = VirtualMember |
|
930 | ||
931 |
def get_form_kwargs(self): |
|
932 |
kwargs = super(VirtualMemberAddView, self).get_form_kwargs() |
|
933 |
kwargs['initial']['virtual_agenda'] = kwargs['initial']['agenda'] |
|
934 |
return kwargs |
|
935 | ||
936 | ||
937 |
agenda_add_virtual_member = VirtualMemberAddView.as_view() |
|
938 | ||
939 | ||
940 |
class VirtualMemberDeleteView(DeleteView): |
|
941 |
template_name = 'chrono/manager_confirm_virtual_member_delete.html' |
|
942 |
model = VirtualMember |
|
943 | ||
944 |
def dispatch(self, request, *args, **kwargs): |
|
945 |
self.agenda = self.get_object().virtual_agenda |
|
946 |
if not self.agenda.can_be_managed(request.user): |
|
947 |
raise PermissionDenied() |
|
948 |
return super(VirtualMemberDeleteView, self).dispatch(request, *args, **kwargs) |
|
949 | ||
950 |
def get_context_data(self, **kwargs): |
|
951 |
context = super(VirtualMemberDeleteView, self).get_context_data(**kwargs) |
|
952 |
context['agenda'] = self.agenda |
|
953 |
return context |
|
954 | ||
955 |
def get_success_url(self): |
|
956 |
return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.agenda.pk}) |
|
957 | ||
958 | ||
959 |
virtual_member_delete = VirtualMemberDeleteView.as_view() |
|
960 | ||
961 | ||
878 | 962 |
class AgendaAddTimePeriodExceptionView(ManagedDeskMixin, CreateView): |
879 | 963 |
template_name = 'chrono/manager_time_period_exception_form.html' |
880 | 964 |
model = TimePeriodException |
tests/test_manager.py | ||
---|---|---|
24 | 24 |
TimePeriod, |
25 | 25 |
TimePeriodException, |
26 | 26 |
TimePeriodExceptionSource, |
27 |
VirtualMember, |
|
27 | 28 |
) |
28 | 29 | |
29 | 30 |
pytestmark = pytest.mark.django_db |
... | ... | |
2114 | 2115 |
resp.form['agendas_json'] = Upload('export.json', agenda_export, 'application/json') |
2115 | 2116 |
resp = resp.form.submit() |
2116 | 2117 |
assert u'Missing "gé1" role' in resp.text |
2118 | ||
2119 | ||
2120 |
def test_virtual_agenda_add(app, admin_user): |
|
2121 |
app = login(app) |
|
2122 |
resp = app.get('/manage/', status=200) |
|
2123 |
resp = resp.click('New') |
|
2124 |
resp.form['label'] = 'Virtual agenda' |
|
2125 |
resp.form['kind'] = 'virtual' |
|
2126 |
resp = resp.form.submit() |
|
2127 |
agenda = Agenda.objects.get(label='Virtual agenda') |
|
2128 |
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id) |
|
2129 | ||
2130 | ||
2131 |
def test_virtual_agenda_baseview_empty(app, admin_user): |
|
2132 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2133 |
app = login(app) |
|
2134 |
resp = app.get(agenda.get_absolute_url()) |
|
2135 |
assert 'Settings' in resp.text |
|
2136 |
assert 'My Virtual agenda' in resp.text |
|
2137 |
assert 'Included Agendas' in resp.text |
|
2138 |
assert 'This virtual agenda is empty.' in resp.text |
|
2139 |
assert '/manage/agendas/%s/settings' % agenda.pk in resp.text |
|
2140 | ||
2141 | ||
2142 |
def test_virtual_agenda_baseview(app, admin_user): |
|
2143 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2144 |
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings') |
|
2145 |
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings') |
|
2146 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1) |
|
2147 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2) |
|
2148 | ||
2149 |
app = login(app) |
|
2150 |
resp = app.get(agenda.get_absolute_url()) |
|
2151 |
assert 'Settings' in resp.text |
|
2152 |
assert 'My Virtual agenda' in resp.text |
|
2153 |
assert 'Included Agendas' in resp.text |
|
2154 |
assert 'This virtual agenda is empty.' not in resp.text |
|
2155 |
for real_agenda in [meeting_agenda_1, meeting_agenda_2]: |
|
2156 |
assert real_agenda.label in resp.text |
|
2157 |
assert real_agenda.slug in resp.text |
|
2158 |
assert real_agenda.get_absolute_url() in resp.text |
|
2159 | ||
2160 | ||
2161 |
def test_virtual_agenda_settings_empty(app, admin_user): |
|
2162 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2163 |
app = login(app) |
|
2164 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2165 |
assert 'Include Agenda' in resp.text |
|
2166 |
assert 'Options' in resp.text |
|
2167 |
assert 'Export' in resp.text |
|
2168 |
assert 'Delete' in resp.text |
|
2169 |
assert 'Included Agendas' in resp.text |
|
2170 |
assert "This virtual agenda doesn't include any agenda yet" in resp.text |
|
2171 |
# No meeting types yet |
|
2172 |
assert 'Meeting Types' not in resp.text |
|
2173 | ||
2174 | ||
2175 |
def test_virtual_agenda_settings(app, admin_user): |
|
2176 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2177 |
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings') |
|
2178 |
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings') |
|
2179 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1) |
|
2180 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2) |
|
2181 |
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10) |
|
2182 |
mt2 = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10) |
|
2183 | ||
2184 |
app = login(app) |
|
2185 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2186 |
assert "This virtual agenda doesn't include any agenda yet" not in resp.text |
|
2187 |
for real_agenda in [meeting_agenda_1, meeting_agenda_2]: |
|
2188 |
assert real_agenda.slug in resp.text |
|
2189 |
assert '/manage/agendas/%s/settings' % real_agenda.pk in resp.text |
|
2190 | ||
2191 |
assert 'Meeting Types' in resp.text |
|
2192 |
assert 'MT' in resp.text |
|
2193 |
assert 'mt' in resp.text |
|
2194 |
assert '10' in resp.text |
|
2195 | ||
2196 |
# Error message when incompatible meeting types |
|
2197 |
mt2.delete() |
|
2198 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2199 |
assert "This virtual agenda doesn't have any meeting type." in resp.text |
|
2200 | ||
2201 | ||
2202 |
def test_virtual_agenda_settings_include(app, admin_user): |
|
2203 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2204 |
Agenda.objects.create(label='Event agenda', kind='events') |
|
2205 |
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings') |
|
2206 |
Agenda.objects.create(label='Meeting agenda 2', kind='meetings') |
|
2207 | ||
2208 |
app = login(app) |
|
2209 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2210 |
resp = resp.click('Include Agenda') |
|
2211 |
# Only meetings agenda are proposed (2) + 1 empty choice = 3 |
|
2212 |
assert len(resp.form['real_agenda'].options) == 3 |
|
2213 |
# Include a real agenda |
|
2214 |
resp.form['real_agenda'].value = meeting_agenda_1.pk |
|
2215 |
resp = resp.form.submit() |
|
2216 |
assert resp.location.endswith('/manage/agendas/%s/settings' % agenda.id) |
|
2217 |
assert VirtualMember.objects.get(virtual_agenda=agenda, real_agenda=meeting_agenda_1) |
|
2218 | ||
2219 |
resp = resp.follow() |
|
2220 |
resp = resp.click('Include Agenda') |
|
2221 |
# The previously include agenda is not proposed any more |
|
2222 |
assert len(resp.form['real_agenda'].options) == 2 |
|
2223 | ||
2224 | ||
2225 |
def test_virtual_agenda_settings_include_incompatible_agenda(app, admin_user): |
|
2226 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2227 |
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings') |
|
2228 |
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10) |
|
2229 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1) |
|
2230 | ||
2231 |
# This one is incompatible because of its meeting type |
|
2232 |
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings') |
|
2233 |
MeetingType.objects.create(agenda=meeting_agenda_2, label='AA', slug='aa', duration=30) |
|
2234 | ||
2235 |
app = login(app) |
|
2236 |
resp = app.get('/manage/agendas/%s/settings' % agenda.pk) |
|
2237 |
resp = resp.click('Include Agenda') |
|
2238 |
resp.form['real_agenda'].value = meeting_agenda_2.pk |
|
2239 |
resp = resp.form.submit() |
|
2240 |
assert 'This agenda does not the meetingtypes provided by the virtualagenda.' in resp.text |
|
2241 |
assert meeting_agenda_2.virtual_agendas.count() == 0 |
|
2242 | ||
2243 | ||
2244 |
def test_cant_delete_meetingtype_used_by_virtual_agenda(app, admin_user): |
|
2245 |
agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual') |
|
2246 |
meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings') |
|
2247 |
meeting_agenda_2 = Agenda.objects.create(label='Meeting agenda 2', kind='meetings') |
|
2248 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_1) |
|
2249 |
VirtualMember.objects.create(virtual_agenda=agenda, real_agenda=meeting_agenda_2) |
|
2250 |
MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10) |
|
2251 |
mt = MeetingType.objects.create(agenda=meeting_agenda_2, label='MT', slug='mt', duration=10) |
|
2252 | ||
2253 |
app = login(app) |
|
2254 |
resp = app.get('/manage/agendas/%s/settings' % meeting_agenda_2.pk) |
|
2255 |
resp = resp.click('MT') |
|
2256 |
resp = resp.click('Delete') |
|
2257 |
assert 'This cannot be removed as it used by a virtual agenda' in resp.text |
|
2258 |
assert 'disabled' in resp.text |
|
2259 | ||
2260 |
resp = app.post('/manage/meetingtypes/%s/delete' % mt.pk, status=403) |
|
2117 |
- |