Merge lp:~cjohnston/summit/cj-wrote-tests into lp:summit

Proposed by Chris Johnston
Status: Merged
Approved by: Michael Hall
Approved revision: 496
Merged at revision: 484
Proposed branch: lp:~cjohnston/summit/cj-wrote-tests
Merge into: lp:summit
Diff against target: 1362 lines (+866/-111)
10 files modified
summit/schedule/models/meetingmodel.py (+175/-64)
summit/schedule/models/participantmodel.py (+26/-14)
summit/schedule/models/roommodel.py (+7/-2)
summit/schedule/models/summitmodel.py (+1/-1)
summit/schedule/models/trackmodel.py (+6/-1)
summit/schedule/templates/schedule/virtual_meeting.html (+2/-0)
summit/schedule/tests/__init__.py (+1/-0)
summit/schedule/tests/test_virtual.py (+572/-0)
summit/schedule/tests/tests.py (+8/-2)
summit/schedule/views.py (+68/-27)
To merge this branch: bzr merge lp:~cjohnston/summit/cj-wrote-tests
Reviewer Review Type Date Requested Status
Michael Hall (community) Approve
Review via email: mp+150191@code.launchpad.net

Commit message

Adds 22 tests for the new virtual meeting functionality.

To post a comment you must log in.
lp:~cjohnston/summit/cj-wrote-tests updated
488. By Chris Johnston

More test stuff

489. By Chris Johnston

Reducing code

490. By Chris Johnston

more tests

491. By Chris Johnston

More tests

492. By Chris Johnston

more

493. By Chris Johnston

fixes from Michael

494. By Chris Johnston

save form

495. By Chris Johnston

Let us test forms

496. By Chris Johnston

Testing tests

Revision history for this message
Michael Hall (mhall119) wrote :

162 tests, all pass, great job

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'summit/schedule/models/meetingmodel.py'
2--- summit/schedule/models/meetingmodel.py 2013-02-20 13:57:06 +0000
3+++ summit/schedule/models/meetingmodel.py 2013-02-26 19:00:29 +0000
4@@ -1,5 +1,5 @@
5 # The Summit Scheduler web application
6-# Copyright (C) 2008 - 2012 Ubuntu Community, Canonical Ltd
7+# Copyright (C) 2008 - 2013 Ubuntu Community, Canonical Ltd
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU Affero General Public License as
11@@ -21,7 +21,6 @@
12
13 from django.db import models
14 from django.core.exceptions import ObjectDoesNotExist
15-from django.conf import settings
16 from django.core.urlresolvers import reverse
17
18 import simplejson as json
19@@ -91,15 +90,31 @@
20 )
21
22 summit = models.ForeignKey(Summit)
23- name = NameField(max_length=100, help_text="Lowercase alphanumeric characters and dashes only.")
24- title = models.CharField(max_length=100, help_text="Alphanumeric characters and spaces are allowed")
25+ name = NameField(
26+ max_length=100,
27+ help_text="Lowercase alphanumeric characters and dashes only."
28+ )
29+ title = models.CharField(
30+ max_length=100,
31+ help_text="Alphanumeric characters and spaces are allowed"
32+ )
33 description = models.TextField(max_length=2047, blank=True)
34- type = models.CharField(max_length=15, choices=TYPE_CHOICES,
35- default=TYPE_CHOICES[0][0])
36- status = models.CharField(max_length=10, choices=STATUS_CHOICES,
37- null=True, blank=True)
38- priority = models.IntegerField(choices=PRIORITY_CHOICES,
39- null=True, blank=True)
40+ type = models.CharField(
41+ max_length=15,
42+ choices=TYPE_CHOICES,
43+ default=TYPE_CHOICES[0][0]
44+ )
45+ status = models.CharField(
46+ max_length=10,
47+ choices=STATUS_CHOICES,
48+ null=True,
49+ blank=True
50+ )
51+ priority = models.IntegerField(
52+ choices=PRIORITY_CHOICES,
53+ null=True,
54+ blank=True
55+ )
56 hangout_url = models.URLField(
57 blank=True,
58 null=True,
59@@ -113,20 +128,37 @@
60 # FIXME tracks must be for the same summit
61 # (will require js magic in admin to refresh the boxes)
62 tracks = models.ManyToManyField(Track, blank=True)
63- spec_url = models.URLField(blank=True,
64- verbose_name="Spec URL")
65- wiki_url = models.URLField(blank=True, verify_exists=False,
66- verbose_name="Wiki URL")
67- pad_url = models.URLField(verify_exists=False,
68- verbose_name="Pad URL", null=True, blank=True)
69+ spec_url = models.URLField(
70+ blank=True,
71+ verbose_name="Spec URL"
72+ )
73+ wiki_url = models.URLField(
74+ blank=True,
75+ verify_exists=False,
76+ verbose_name="Wiki URL"
77+ )
78+ pad_url = models.URLField(
79+ verify_exists=False,
80+ verbose_name="Pad URL",
81+ null=True,
82+ blank=True
83+ )
84 slots = models.IntegerField(default=1)
85- override_break = models.BooleanField(default=False, verbose_name="Override Break",
86- help_text="If this is a multi-slot meeting, should it be allowed to take place during a break")
87+ override_break = models.BooleanField(
88+ default=False,
89+ verbose_name="Override Break",
90+ help_text="If this is a multi-slot meeting, should it be"
91+ "allowed to take place during a break"
92+ )
93 virtual_meeting = models.BooleanField(
94 help_text="If virtual summit is true, this will not override it"
95 )
96- approved = models.CharField(max_length=10, choices=REVIEW_CHOICES,
97- null=True, default='PENDING')
98+ approved = models.CharField(
99+ max_length=10,
100+ choices=REVIEW_CHOICES,
101+ null=True,
102+ default='PENDING'
103+ )
104 private = models.BooleanField(default=False)
105 private_key = models.CharField(max_length=32, null=True, blank=True)
106
107@@ -142,16 +174,35 @@
108
109 # FIXME attendees must be for the same summit
110 # (will require js magic in admin to refresh the boxes)
111- drafter = models.ForeignKey(Attendee, null=True, blank=True,
112- related_name='drafter_set')
113- assignee = models.ForeignKey(Attendee, null=True, blank=True,
114- related_name='assignee_set')
115- approver = models.ForeignKey(Attendee, null=True, blank=True,
116- related_name='approver_set')
117- scribe = models.ForeignKey(Attendee, null=True, blank=True,
118- related_name='scribe_set')
119- participants = models.ManyToManyField(Attendee, blank=True,
120- through='Participant')
121+ drafter = models.ForeignKey(
122+ Attendee,
123+ null=True,
124+ blank=True,
125+ related_name='drafter_set'
126+ )
127+ assignee = models.ForeignKey(
128+ Attendee,
129+ null=True,
130+ blank=True,
131+ related_name='assignee_set'
132+ )
133+ approver = models.ForeignKey(
134+ Attendee,
135+ null=True,
136+ blank=True,
137+ related_name='approver_set'
138+ )
139+ scribe = models.ForeignKey(
140+ Attendee,
141+ null=True,
142+ blank=True,
143+ related_name='scribe_set'
144+ )
145+ participants = models.ManyToManyField(
146+ Attendee,
147+ blank=True,
148+ through='Participant'
149+ )
150 launchpad_blueprint_id = models.IntegerField(blank=True, null=True)
151
152 class Meta:
153@@ -169,7 +220,8 @@
154 return
155
156 if not self.private_key:
157- import hashlib, random
158+ import hashlib
159+ import random
160 meeting_hash = hashlib.md5()
161 meeting_hash.update(str(self.pk))
162 meeting_hash.update(self.name)
163@@ -185,7 +237,8 @@
164 if self.pad_url is not None and self.pad_url != '':
165 return self.pad_url
166 elif self.private:
167- import hashlib, random
168+ import hashlib
169+ import random
170 meeting_hash = hashlib.md5()
171 meeting_hash.update(str(self.pk))
172 meeting_hash.update(self.name)
173@@ -362,7 +415,10 @@
174 # fetch to update the title
175 # Now, LP has an API, we're stopping the sceen scrape :)
176 try:
177- apiurl = spec_url.replace('blueprints.launchpad.net', 'api.launchpad.net/devel') + '?ws.accept=application/json'
178+ apiurl = spec_url.replace(
179+ 'blueprints.launchpad.net',
180+ 'api.launchpad.net/devel'
181+ ) + '?ws.accept=application/json'
182 lpdata = urllib2.urlopen(apiurl)
183 data = json.load(lpdata)
184 if 'title' in data:
185@@ -397,7 +453,8 @@
186 if assignee:
187 try:
188 self.assignee = self.summit.attendee_set.get(
189- user__username__exact=assignee)
190+ user__username__exact=assignee
191+ )
192 except ObjectDoesNotExist:
193 pass
194
195@@ -406,7 +463,8 @@
196 if approver:
197 try:
198 self.approver = self.summit.attendee_set.get(
199- user__username__exact=approver)
200+ user__username__exact=approver
201+ )
202 except ObjectDoesNotExist:
203 pass
204
205@@ -422,18 +480,21 @@
206
207 try:
208 attendee = self.summit.attendee_set.get(
209- user__username__exact=username)
210+ user__username__exact=username
211+ )
212
213 try:
214 participant = self.participant_set.get(
215- attendee=attendee)
216+ attendee=attendee
217+ )
218 except ObjectDoesNotExist:
219 participant = self.participant_set.create(
220- attendee=attendee)
221+ attendee=attendee
222+ )
223 except:
224 participant = self.participant_set.filter(
225- attendee=attendee)[0]
226-
227+ attendee=attendee
228+ )[0]
229
230 participant.from_launchpad = True
231 if required:
232@@ -444,11 +505,15 @@
233 except ObjectDoesNotExist:
234 pass
235
236- self.participant_set.filter(from_launchpad=True).filter(~models.Q(attendee__user__username__in=from_lp)).delete()
237+ self.participant_set.filter(
238+ from_launchpad=True
239+ ).filter(
240+ ~models.Q(attendee__user__username__in=from_lp)
241+ ).delete()
242
243 self.save()
244
245- def check_schedule(self, slot, room, with_interested = False):
246+ def check_schedule(self, slot, room, with_interested=False):
247 """Check whether we can schedule this meeting in the given slot
248 and room."""
249 missing = set()
250@@ -456,7 +521,8 @@
251 slots = [slot]
252 if self.slots > 1:
253 slots.extend(s for s
254- in self.summit.slot_set.filter(start_utc__gte=slot.end_utc).order_by('start_utc')
255+ in self.summit.slot_set.filter(
256+ start_utc__gte=slot.end_utc).order_by('start_utc')
257 if s.start.date() == slot.start.date())
258 if len(slots) < self.slots:
259 raise Meeting.SchedulingError("Insufficient slots available")
260@@ -470,16 +536,23 @@
261 continue
262 if this_slot.type not in ('open', 'plenary'):
263 raise Meeting.SchedulingError("Slot not available")
264- if this_slot.type == 'plenary' and self.type not in ('plenary', 'talk', 'special'):
265+ if this_slot.type == 'plenary' and self.type not in (
266+ 'plenary',
267+ 'talk',
268+ 'special'
269+ ):
270 raise Meeting.SchedulingError(
271- "Not a plenary event, talk or special event")
272+ "Not a plenary event, talk or special event"
273+ )
274
275 # Check that the room is not already in use
276 try:
277 existing = room.agenda_set.get(slot=this_slot)
278 if existing.meeting != self:
279- raise Meeting.SchedulingError("Room is in use by %s"
280- % existing.meeting)
281+ raise Meeting.SchedulingError(
282+ "Room is in use by %s"
283+ % existing.meeting
284+ )
285 except ObjectDoesNotExist:
286 pass
287
288@@ -496,8 +569,10 @@
289 for agenda in prev_slot.agenda_set.filter(room=room):
290 if agenda.meeting != self \
291 and agenda.meeting.slots >= (distance - breaks):
292- raise Meeting.SchedulingError("Room is in use by %s"
293- % agenda.meeting)
294+ raise Meeting.SchedulingError(
295+ "Room is in use by %s"
296+ % agenda.meeting
297+ )
298
299 # Check that the room is available
300 if not room.available(this_slot):
301@@ -505,15 +580,21 @@
302
303 if self.requires_dial_in:
304 if not room.has_dial_in:
305- raise Meeting.SchedulingError("Room has no dial-in capability")
306+ raise Meeting.SchedulingError(
307+ "Room has no dial-in capability"
308+ )
309
310 # Work out who is busy in this slot
311 busy = set()
312 for agenda in this_slot.agenda_set.all():
313 if agenda.meeting != self:
314- busy.update([a.pk for a in agenda.meeting.required_attendees])
315+ busy.update(
316+ [a.pk for a in agenda.meeting.required_attendees]
317+ )
318 if with_interested:
319- busy.update([a.pk for a in agenda.meeting.get_participants_by_level('INTERESTED')])
320+ busy.update(
321+ [a.pk for a in agenda.meeting.get_participants_by_level('INTERESTED')]
322+ )
323 slot_num = all_slots.index(this_slot)
324 for i in range(slot_num, 0, -1):
325 distance = 2 + slot_num - i
326@@ -524,9 +605,13 @@
327 for agenda in prev_slot.agenda_set.all():
328 if agenda.meeting != self \
329 and agenda.meeting.slots >= distance:
330- busy.update([a.pk for a in agenda.meeting.required_attendees])
331+ busy.update(
332+ [a.pk for a in agenda.meeting.required_attendees]
333+ )
334 if with_interested:
335- busy.update([a.pk for a in agenda.meeting.get_participants_by_level('INTERESTED')])
336+ busy.update(
337+ [a.pk for a in agenda.meeting.get_participants_by_level('INTERESTED')]
338+ )
339 # Check that all required attendees are free in this slot
340 for attendee in self.required_attendees:
341 if not attendee.available(this_slot):
342@@ -544,12 +629,21 @@
343 def check_not_same_track(other_slot, relative_position):
344 type_exceptions = ('presentation', 'plenary', 'talk')
345 if other_slot is not None and self.type not in type_exceptions:
346- for agenda in other_slot.agenda_set.filter(room__exact=room).exclude(
347- meeting__exact=self).filter(meeting__tracks__in=self.tracks.all()):
348+ for agenda in other_slot.agenda_set.filter(
349+ room__exact=room
350+ ).exclude(
351+ meeting__exact=self
352+ ).filter(
353+ meeting__tracks__in=self.tracks.all()
354+ ):
355 if agenda.meeting.tracks.filter(
356- pk__in=self.tracks.all()).exclude(allow_adjacent_sessions=True).count() > 0:
357+ pk__in=self.tracks.all()
358+ ).exclude(
359+ allow_adjacent_sessions=True
360+ ).count() > 0:
361 raise Meeting.SchedulingError(
362- "Same track in the %s slot" % relative_position)
363+ "Same track in the %s slot" % relative_position
364+ )
365
366 check_not_same_track(this_slot.previous(), "previous")
367 check_not_same_track(this_slot.next(), "next")
368@@ -583,21 +677,38 @@
369 """Try to schedule this meeting in one of the given rooms."""
370 today = datetime.now()
371 for room in rooms:
372- for slot in self.summit.slot_set.filter(type__exact='open',start_utc__gt=today):
373+ for slot in self.summit.slot_set.filter(
374+ type__exact='open',
375+ start_utc__gt=today
376+ ):
377 try:
378 room.agenda_set.get(slot=slot)
379 except room.agenda_set.model.DoesNotExist:
380 try:
381- missing = self.check_schedule(slot, room, with_interested)
382+ missing = self.check_schedule(
383+ slot,
384+ room,
385+ with_interested
386+ )
387 if len(missing):
388- raise Meeting.SchedulingError("Required people not available: %s"
389- % ', '.join(m.user.username for m in missing))
390+ raise Meeting.SchedulingError(
391+ "Required people not available: %s"
392+ % ', '.join(m.user.username for m in missing)
393+ )
394
395 print "Schedule %s in %s at %s" % (self, room, slot)
396 room.agenda_set.create(
397- slot=slot, meeting=self, auto=True)
398+ slot=slot,
399+ meeting=self,
400+ auto=True
401+ )
402 return True
403 except Meeting.SchedulingError, e:
404- print "-- could not schedule %s in %s at %s (%s)" % (self, room, slot, e)
405+ print "-- could not schedule %s in %s at %s (%s)" % (
406+ self,
407+ room,
408+ slot,
409+ e
410+ )
411
412 return False
413
414=== modified file 'summit/schedule/models/participantmodel.py'
415--- summit/schedule/models/participantmodel.py 2012-07-12 18:36:38 +0000
416+++ summit/schedule/models/participantmodel.py 2013-02-26 19:00:29 +0000
417@@ -1,5 +1,5 @@
418 # The Summit Scheduler web application
419-# Copyright (C) 2008 - 2012 Ubuntu Community, Canonical Ltd
420+# Copyright (C) 2008 - 2013 Ubuntu Community, Canonical Ltd
421 #
422 # This program is free software: you can redistribute it and/or modify
423 # it under the terms of the GNU Affero General Public License as
424@@ -25,24 +25,30 @@
425
426
427 HELP_TEXT = {
428- "attendee": ("Be sure to choose the attendee from the correct "
429+ "attendee": (
430+ "Be sure to choose the attendee from the correct "
431 "event. If the person is not listed for the event in question "
432 "then they are not known to be attending yet, and have to "
433- "sign up to attend."),
434- "required": ("The person is required to attend the session. "
435+ "sign up to attend."
436+ ),
437+ "required": (
438+ "The person is required to attend the session. "
439 "If this is set the scheduler will attempt to ensure that "
440 "the person is able to attend. If it is not checked then "
441 "the scheduler won't consider the person when deciding "
442- "where the meeting can go on the schedule."),
443- "from_launchpad": ("Set to indicate the participant comes from "
444+ "where the meeting can go on the schedule."
445+ ),
446+ "from_launchpad": (
447+ "Set to indicate the participant comes from "
448 "a subscription to the Launchpad blueprint. Don't set this "
449 "if you are adding the participant, as they may end up "
450- "getting deleted if you do."),
451+ "getting deleted if you do."
452+ ),
453 }
454
455
456 class Participant(models.Model):
457-
458+
459 PARTICIPATION_CHOICES = (
460 (u'ATTENDING', u'Attending'),
461 (u'INTERESTED', u'Very interested in attending'),
462@@ -50,12 +56,18 @@
463 )
464
465 meeting = models.ForeignKey(Meeting)
466- attendee = models.ForeignKey(Attendee,
467- help_text=HELP_TEXT["attendee"])
468- participation = models.CharField(max_length=32, choices=PARTICIPATION_CHOICES,
469- null=True, blank=False, default='ATTENDING')
470- from_launchpad = models.BooleanField(default=False,
471- help_text=HELP_TEXT["from_launchpad"])
472+ attendee = models.ForeignKey(Attendee, help_text=HELP_TEXT["attendee"])
473+ participation = models.CharField(
474+ max_length=32,
475+ choices=PARTICIPATION_CHOICES,
476+ null=True,
477+ blank=False,
478+ default='ATTENDING'
479+ )
480+ from_launchpad = models.BooleanField(
481+ default=False,
482+ help_text=HELP_TEXT["from_launchpad"]
483+ )
484
485 class Meta:
486 app_label = 'schedule'
487
488=== modified file 'summit/schedule/models/roommodel.py'
489--- summit/schedule/models/roommodel.py 2012-02-06 18:25:01 +0000
490+++ summit/schedule/models/roommodel.py 2013-02-26 19:00:29 +0000
491@@ -51,8 +51,13 @@
492 verbose_name="End (UTC)")
493 icecast_url = models.URLField(blank=True, verify_exists=False,
494 verbose_name="Icecast URL")
495- irc_channel = models.CharField(max_length=50, verbose_name="IRC Channel", blank=True, help_text="Please enter the IRC channel without the #")
496-
497+ irc_channel = models.CharField(
498+ max_length=50,
499+ verbose_name="IRC Channel",
500+ blank=True,
501+ help_text="Please enter the IRC channel without the #"
502+ )
503+
504 # Whether the room has dial-in capability
505 has_dial_in = models.BooleanField(default=False)
506
507
508=== modified file 'summit/schedule/models/summitmodel.py'
509--- summit/schedule/models/summitmodel.py 2013-02-21 23:10:17 +0000
510+++ summit/schedule/models/summitmodel.py 2013-02-26 19:00:29 +0000
511@@ -1,5 +1,5 @@
512 # The Summit Scheduler web application
513-# Copyright (C) 2008 - 2012 Ubuntu Community, Canonical Ltd
514+# Copyright (C) 2008 - 2013 Ubuntu Community, Canonical Ltd
515 #
516 # This program is free software: you can redistribute it and/or modify
517 # it under the terms of the GNU Affero General Public License as
518
519=== modified file 'summit/schedule/models/trackmodel.py'
520--- summit/schedule/models/trackmodel.py 2012-12-06 22:24:06 +0000
521+++ summit/schedule/models/trackmodel.py 2013-02-26 19:00:29 +0000
522@@ -28,7 +28,12 @@
523 summit = models.ForeignKey(Summit)
524 title = models.CharField(max_length=100)
525 slug = models.CharField(max_length=100, null=True, blank=False)
526- color = models.CharField(max_length=6, null=False, blank=False, default='FFFFFF')
527+ color = models.CharField(
528+ max_length=6,
529+ null=False,
530+ blank=False,
531+ default='FFFFFF'
532+ )
533 description = models.TextField(max_length=1000, blank=False, null=True)
534 allow_adjacent_sessions = models.BooleanField(default=False)
535
536
537=== modified file 'summit/schedule/templates/schedule/virtual_meeting.html'
538--- summit/schedule/templates/schedule/virtual_meeting.html 2013-02-21 23:10:17 +0000
539+++ summit/schedule/templates/schedule/virtual_meeting.html 2013-02-26 19:00:29 +0000
540@@ -72,7 +72,9 @@
541 <h2>{{ meeting.title }}</h2>
542 <div id="description">
543 {{ meeting.description|markdown:'safe' }}
544+ {% if required %}
545 <h3><a href="{{ meeting.hangout_url }}">Join the Hangout on Air</a></h3>
546+ {% endif %}
547 </div>
548
549 </section>
550
551=== modified file 'summit/schedule/tests/__init__.py'
552--- summit/schedule/tests/__init__.py 2013-02-22 01:22:19 +0000
553+++ summit/schedule/tests/__init__.py 2013-02-26 19:00:29 +0000
554@@ -15,3 +15,4 @@
555 # along with this program. If not, see <http://www.gnu.org/licenses/>.
556
557 from tests import *
558+from test_virtual import *
559
560=== added file 'summit/schedule/tests/test_virtual.py'
561--- summit/schedule/tests/test_virtual.py 1970-01-01 00:00:00 +0000
562+++ summit/schedule/tests/test_virtual.py 2013-02-26 19:00:29 +0000
563@@ -0,0 +1,572 @@
564+# The Summit Scheduler web application
565+# Copyright (C) 2008 - 2013 Ubuntu Community, Canonical Ltd
566+#
567+# This program is free software: you can redistribute it and/or modify
568+# it under the terms of the GNU Affero General Public License as
569+# published by the Free Software Foundation, either version 3 of the
570+# License, or (at your option) any later version.
571+#
572+# This program is distributed in the hope that it will be useful,
573+# but WITHOUT ANY WARRANTY; without even the implied warranty of
574+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
575+# GNU Affero General Public License for more details.
576+#
577+# You should have received a copy of the GNU Affero General Public License
578+# along with this program. If not, see <http://www.gnu.org/licenses/>.
579+
580+import datetime
581+
582+from django import test as djangotest
583+from django.core.urlresolvers import reverse
584+from django.contrib.auth.models import User
585+from django.test.client import Client
586+
587+from model_mommy import mommy as factory
588+
589+from summit.schedule.models import (
590+ Summit,
591+ Slot,
592+ Attendee,
593+ Meeting,
594+ Track,
595+ Room,
596+ Lead,
597+ Participant,
598+ Agenda,
599+)
600+
601+
602+class VirtualSummitTestCase(djangotest.TestCase):
603+ """
604+ Tests for the virtual meeting display
605+ """
606+
607+ c = Client()
608+
609+ def setUp(self):
610+ now = datetime.datetime.utcnow()
611+ one_hour = datetime.timedelta(0, 3600)
612+ week = datetime.timedelta(days=5)
613+ self.summit = factory.make_one(
614+ Summit,
615+ name='uds-virt',
616+ virtual_summit=True,
617+ date_start=now,
618+ date_end=now + week,
619+ )
620+
621+ self.slot = factory.make_one(
622+ Slot,
623+ start_utc=now+one_hour,
624+ end_utc=now+(2*one_hour),
625+ type='open',
626+ summit=self.summit
627+ )
628+ self.track = factory.make_one(Track, summit=self.summit)
629+ self.room = factory.make_one(
630+ Room,
631+ summit=self.summit,
632+ type='open',
633+ irc_channel='test-channel',
634+ )
635+
636+ self.meeting1 = factory.make_one(
637+ Meeting,
638+ summit=self.summit,
639+ name='meeting-non-virt',
640+ private=False,
641+ requires_dial_in=False,
642+ spec_url='',
643+ pad_url='http://pad.com/1'
644+ )
645+ self.meeting1.tracks = [self.track]
646+
647+ self.user1 = factory.make_one(
648+ User,
649+ username='testuser',
650+ first_name='Test',
651+ last_name='User',
652+ is_active=True,
653+ is_superuser=False,
654+ )
655+ self.user1.set_password('password')
656+ self.user1.save()
657+ self.attendee1 = factory.make_one(
658+ Attendee,
659+ summit=self.summit,
660+ user=self.user1,
661+ start_utc=now,
662+ end_utc=now+week
663+ )
664+
665+ self.agenda1 = factory.make_one(
666+ Agenda,
667+ slot=self.slot,
668+ meeting=self.meeting1,
669+ room=self.room
670+ )
671+
672+ def tearDown(self):
673+ self.client.logout()
674+
675+ def login(self):
676+ logged_in = self.c.login(
677+ username='testuser',
678+ password='password')
679+ self.assertTrue(logged_in)
680+
681+ def view_and_save_edit_meeting_hangout_form(self):
682+ """
683+ Tests that a user with proper permissions can save the
684+ edit_meeting_hangout form
685+ """
686+ # View the edit meeting hangout form
687+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
688+ response = self.c.get(
689+ reverse(
690+ 'summit.schedule.views.edit_meeting_hangout',
691+ args=rev_args
692+ )
693+ )
694+ self.assertEqual(response.status_code, 200)
695+ self.assertTemplateUsed(response, 'schedule/edit_hangout.html')
696+
697+ # Define data to fill our the form
698+ hangout_url = 'http://hangouturl.com'
699+ broadcast_url = 'http://broadcasturl.com'
700+
701+ # Post the form
702+ post = self.c.post(
703+ reverse(
704+ 'summit.schedule.views.edit_meeting_hangout',
705+ args=rev_args
706+ ),
707+ data={
708+ 'broadcast_url': broadcast_url,
709+ 'hangout_url': hangout_url,
710+ }
711+ )
712+
713+ # A successful post should redirect to the meeting page
714+ response = self.view_virtual_meeting_page()
715+ redirect_url = reverse(
716+ 'summit.schedule.views.meeting',
717+ args=rev_args
718+ )
719+ self.assertEqual(post.status_code, 302)
720+ self.assertRedirects(
721+ post,
722+ redirect_url,
723+ )
724+ redirect_responseonse = self.c.get(redirect_url)
725+
726+ # Verify that the new data appears on the meeting page
727+ self.assertEqual(redirect_responseonse.status_code, 200)
728+ self.assertIn(broadcast_url, redirect_responseonse.content)
729+ self.assertIn(hangout_url, redirect_responseonse.content)
730+ self.assertIn('Join the Hangout on Air', redirect_responseonse.content)
731+
732+ def view_virtual_meeting_page(self):
733+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
734+ response = self.c.get(
735+ reverse(
736+ 'summit.schedule.views.meeting',
737+ args=rev_args
738+ )
739+ )
740+ self.assertEqual(response.status_code, 200)
741+ self.assertTemplateUsed(response, 'schedule/virtual_meeting.html')
742+ self.assertTemplateNotUsed(response, 'schedule/meeting.html')
743+
744+ return response
745+
746+ def test_virtual_mtg_true_display_virtual_page(self):
747+ """
748+ Tests that a meeting with virtual_summit=True and virtual_meeting=True
749+ displays the virtual meeting page.
750+ """
751+
752+ self.meeting1.virtual_meeting = True
753+ self.meeting1.save()
754+
755+ response = self.view_virtual_meeting_page()
756+
757+ def test_virtual_mtg_false_display_virtual_page(self):
758+ """
759+ Tests that a meeting with virtual_summit=True and virtual_meeting=False
760+ displays the virtual meeting page.
761+ """
762+
763+ self.meeting1.virtual_meeting = False
764+ self.meeting1.save()
765+ response = self.view_virtual_meeting_page()
766+
767+ def test_lead_has_edit_meeting_hangout_link(self):
768+ """
769+ Tests that the track lead for the meeting has the link to add the
770+ hangout URLs
771+ """
772+
773+ self.lead = factory.make_one(
774+ Lead,
775+ summit=self.summit,
776+ track=self.track,
777+ lead=self.attendee1,
778+ )
779+
780+ self.login()
781+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
782+ response = self.view_virtual_meeting_page()
783+ self.assertIn(
784+ reverse(
785+ 'summit.schedule.views.edit_meeting_hangout',
786+ args=rev_args
787+ ),
788+ response.content
789+ )
790+ self.assertIn('Edit Hangout Details', response.content)
791+
792+ def test_drafter_has_edit_meeting_hangout_link(self):
793+ """
794+ Tests that the meeting drafter has the link to add hangout URLs
795+ """
796+
797+ self.meeting1.drafter = self.attendee1
798+ self.meeting1.save()
799+
800+ self.login()
801+
802+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
803+ response = self.view_virtual_meeting_page()
804+ self.assertContains(
805+ response,
806+ reverse(
807+ 'summit.schedule.views.edit_meeting_hangout',
808+ args=rev_args
809+ ),
810+ 1
811+ )
812+ self.assertContains(response, 'Edit Hangout Details', 1)
813+
814+ def test_manager_has_edit_meeting_hangout_link(self):
815+ """
816+ Tests that a Summit Manager has the link to add hangout URLs
817+ """
818+
819+ self.summit.managers.add(self.user1)
820+
821+ self.login()
822+
823+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
824+ response = self.view_virtual_meeting_page()
825+ self.assertIn(
826+ reverse(
827+ 'summit.schedule.views.edit_meeting_hangout',
828+ args=rev_args
829+ ),
830+ response.content
831+ )
832+ self.assertIn('Edit Hangout Details', response.content)
833+
834+ def test_scheduler_has_edit_meeting_hangout_link(self):
835+ """
836+ Tests that a Summit Scheduler has the link to add hangout URLs
837+ """
838+
839+ self.summit.schedulers.add(self.user1)
840+
841+ self.login()
842+
843+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
844+ response = self.view_virtual_meeting_page()
845+ self.assertIn(
846+ reverse(
847+ 'summit.schedule.views.edit_meeting_hangout',
848+ args=rev_args
849+ ),
850+ response.content
851+ )
852+ self.assertIn('Edit Hangout Details', response.content)
853+
854+ def test_regular_user_does_not__have_edit_meeting_hangout_link(self):
855+ """
856+ Tests that a regular Summit user is not able to see the edit the
857+ hangout link
858+ """
859+
860+ self.login()
861+
862+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
863+ response = self.view_virtual_meeting_page()
864+ self.assertContains(
865+ response,
866+ reverse(
867+ 'summit.schedule.views.edit_meeting_hangout',
868+ args=rev_args
869+ ),
870+ 0
871+ )
872+ self.assertContains(response, 'Edit Hangout Details', 0)
873+
874+ def test_non_logged_in_user_does_not_have_edit_meeting_hangout_link(self):
875+ """
876+ Tests that a non-logged in user is not able to see the edit the hangout
877+ link
878+ """
879+
880+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
881+ response = self.view_virtual_meeting_page()
882+ self.assertContains(
883+ response,
884+ reverse(
885+ 'summit.schedule.views.edit_meeting_hangout',
886+ args=rev_args
887+ ),
888+ 0
889+ )
890+ self.assertContains(response, 'Edit Hangout Details', 0)
891+
892+ def test_lead_can_edit_meeting_hangout(self):
893+ """
894+ Tests that the track lead for the meeting can add the hangout URLs
895+ """
896+
897+ self.lead = factory.make_one(
898+ Lead,
899+ summit=self.summit,
900+ track=self.track,
901+ lead=self.attendee1,
902+ )
903+
904+ self.participant1 = Participant.objects.create(
905+ meeting=self.meeting1,
906+ attendee=self.attendee1,
907+ participation='REQUIRED'
908+ )
909+
910+ self.login()
911+
912+ self.view_and_save_edit_meeting_hangout_form()
913+
914+ def test_drafter_can_edit_meeting_hangout(self):
915+ """
916+ Tests that the meeting drafter can add hangout URLs
917+ """
918+
919+ self.meeting1.drafter = self.attendee1
920+ self.meeting1.save()
921+
922+ self.participant1 = Participant.objects.create(
923+ meeting=self.meeting1,
924+ attendee=self.attendee1,
925+ participation='REQUIRED'
926+ )
927+ self.login()
928+
929+ self.view_and_save_edit_meeting_hangout_form()
930+
931+ def test_manager_can_edit_meeting_hangout(self):
932+ """
933+ Tests that a Summit Manager can add hangout URLs
934+ """
935+
936+ self.summit.managers.add(self.user1)
937+
938+ self.participant1 = Participant.objects.create(
939+ meeting=self.meeting1,
940+ attendee=self.attendee1,
941+ participation='REQUIRED'
942+ )
943+ self.login()
944+
945+ self.view_and_save_edit_meeting_hangout_form()
946+
947+ def test_scheduler_can_edit_meeting_hangout(self):
948+ """
949+ Tests that a Summit Scheduler can add hangout URLs
950+ """
951+
952+ self.summit.schedulers.add(self.user1)
953+
954+ self.participant1 = Participant.objects.create(
955+ meeting=self.meeting1,
956+ attendee=self.attendee1,
957+ participation='REQUIRED'
958+ )
959+ self.login()
960+
961+ self.view_and_save_edit_meeting_hangout_form()
962+
963+ def test_regular_user_can_not_edit_meeting_hangout(self):
964+ """
965+ Tests that a regular Summit user is not able to edit the hangout
966+ links
967+ """
968+
969+ self.login()
970+
971+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
972+ response = self.c.get(
973+ reverse(
974+ 'summit.schedule.views.edit_meeting_hangout',
975+ args=rev_args
976+ ),
977+ )
978+ self.assertEqual(response.status_code, 302)
979+ redirect_args = [self.summit.name, ]
980+ redirect_url = reverse(
981+ 'summit.schedule.views.summit',
982+ args=redirect_args
983+ )
984+ self.assertEqual(
985+ response['Location'],
986+ 'http://testserver' + redirect_url,
987+ )
988+
989+ def test_non_logged_in_user_can_not_edit_meeting_hangout(self):
990+ """
991+ Tests that a non-logged in user is not able to edit the hangout
992+ links
993+ """
994+
995+ rev_args = [self.summit.name, self.meeting1.id, self.meeting1.name]
996+ response = self.c.get(
997+ reverse(
998+ 'summit.schedule.views.edit_meeting_hangout',
999+ args=rev_args
1000+ ),
1001+ )
1002+ self.assertEqual(response.status_code, 302)
1003+ redirect_url = reverse(
1004+ 'summit.schedule.views.edit_meeting_hangout',
1005+ args=rev_args
1006+ )
1007+ self.assertEqual(
1008+ response['Location'],
1009+ 'http://testserver/openid/login?next=' + redirect_url,
1010+ )
1011+
1012+ def test_webchat(self):
1013+ """
1014+ Tests that the webchat client is displayed on the virtual meeting page
1015+ with the proper room
1016+ """
1017+
1018+ response = self.view_virtual_meeting_page()
1019+ self.assertIn(
1020+ 'http://webchat.freenode.net?channels=' +
1021+ self.room.irc_channel +
1022+ '&uio=Mj10cnVlJjQ9dHJ1ZSY5PXRydWUmMTA9' +
1023+ 'dHJ1ZSYxMz1mYWxzZSYxND1mYWxzZQbf',
1024+ response.content
1025+ )
1026+
1027+ def test_hangout_broadcast_url(self):
1028+ """
1029+ Tests that the hangout broadcast is displayed on the page
1030+ """
1031+
1032+ self.meeting1.broadcast_url = 'http://broadcasturl.com'
1033+ self.meeting1.save()
1034+
1035+ response = self.view_virtual_meeting_page()
1036+ self.assertIn(self.meeting1.broadcast_url, response.content)
1037+
1038+ def test_participant_essential_join_url(self):
1039+ """
1040+ Tests that a participant who is essential is shown the link to join
1041+ the hangout
1042+ """
1043+
1044+ self.meeting1.hangout_url = 'http://hangouturl.com'
1045+ self.meeting1.save()
1046+
1047+ self.participant1 = Participant.objects.create(
1048+ meeting=self.meeting1,
1049+ attendee=self.attendee1,
1050+ participation='REQUIRED'
1051+ )
1052+
1053+ self.login()
1054+
1055+ response = self.view_virtual_meeting_page()
1056+ self.assertIn(self.meeting1.hangout_url, response.content)
1057+ self.assertIn('Join the Hangout on Air', response.content)
1058+
1059+ def test_interested_participant_no_join_url(self):
1060+ """
1061+ Tests that a non-essential participant is not shown the link to join
1062+ the hangout
1063+ """
1064+
1065+ self.meeting1.hangout_url = 'http://hangouturl.com'
1066+ self.meeting1.save()
1067+
1068+ self.participant1 = Participant.objects.create(
1069+ meeting=self.meeting1,
1070+ attendee=self.attendee1,
1071+ participation='INTERESTED'
1072+ )
1073+
1074+ self.login()
1075+
1076+ response = self.view_virtual_meeting_page()
1077+ self.assertNotIn(self.meeting1.hangout_url, response.content)
1078+ self.assertNotIn('Join the Hangout on Air', response.content)
1079+
1080+ def test_attending_participant_no_join_url(self):
1081+ """
1082+ Tests that a non-essential (attending) participant is not shown
1083+ the link to join the hangout
1084+ """
1085+
1086+ self.meeting1.hangout_url = 'http://hangouturl.com'
1087+ self.meeting1.save()
1088+
1089+ self.participant1 = Participant.objects.create(
1090+ meeting=self.meeting1,
1091+ attendee=self.attendee1,
1092+ participation='ATTENDING'
1093+ )
1094+
1095+ self.login()
1096+
1097+ response = self.view_virtual_meeting_page()
1098+ self.assertNotIn(self.meeting1.hangout_url, response.content)
1099+ self.assertNotIn('Join the Hangout on Air', response.content)
1100+
1101+ def test_non_participant_no_join_url(self):
1102+ """
1103+ Tests that a participant not attending the meeting is not shown the
1104+ link to join the hangout
1105+ """
1106+
1107+ self.meeting1.hangout_url = 'http://hangouturl.com'
1108+ self.meeting1.save()
1109+
1110+ self.login()
1111+
1112+ response = self.view_virtual_meeting_page()
1113+ self.assertNotIn(self.meeting1.hangout_url, response.content)
1114+ self.assertNotIn('Join the Hangout on Air', response.content)
1115+
1116+ def test_not_logged_in_no_join_url(self):
1117+ """
1118+ Tests that a non-essential participant is not shown the link to join
1119+ the hangout
1120+ """
1121+
1122+ self.meeting1.hangout_url = 'http://hangouturl.com'
1123+ self.meeting1.save()
1124+
1125+ response = self.view_virtual_meeting_page()
1126+ self.assertNotIn(self.meeting1.hangout_url, response.content)
1127+ self.assertNotIn('Join the Hangout on Air', response.content)
1128+
1129+ def test_etherpad(self):
1130+ """
1131+ Tests that the etherpad client is displayed on the virtual meeting page
1132+ """
1133+
1134+ response = self.view_virtual_meeting_page()
1135+ self.assertIn(self.meeting1.pad_url, response.content)
1136
1137=== modified file 'summit/schedule/tests/tests.py'
1138--- summit/schedule/tests/tests.py 2013-02-22 01:22:19 +0000
1139+++ summit/schedule/tests/tests.py 2013-02-26 19:00:29 +0000
1140@@ -1728,7 +1728,12 @@
1141 self.assertEquals(0, self.private_meeting.agenda_set.count())
1142
1143 # Private rooms should not ever be autoscheduled
1144- self.assertEquals(0, Agenda.objects.filter(room__type='private').count())
1145+ self.assertEquals(
1146+ 0,
1147+ Agenda.objects.filter(
1148+ room__type='private'
1149+ ).count()
1150+ )
1151
1152 def test_no_available_private_room(self):
1153 '''
1154@@ -1989,7 +1994,8 @@
1155 )
1156
1157 def tearDown(self):
1158- # Cached requests cause render.py to return old data, so clear the cache
1159+ # Cached requests cause render.py to return old data.
1160+ # Let's clear the cache
1161 if hasattr(cache, 'clear'):
1162 cache.clear()
1163 # Older django didn't have .clear, but locmem cache did have ._cull
1164
1165=== modified file 'summit/schedule/views.py'
1166--- summit/schedule/views.py 2013-02-21 23:10:17 +0000
1167+++ summit/schedule/views.py 2013-02-26 19:00:29 +0000
1168@@ -1,5 +1,5 @@
1169 # The Summit Scheduler web application
1170-# Copyright (C) 2008 - 2012 Ubuntu Community, Canonical Ltd
1171+# Copyright (C) 2008 - 2013 Ubuntu Community, Canonical Ltd
1172 #
1173 # This program is free software: you can redistribute it and/or modify
1174 # it under the terms of the GNU Affero General Public License as
1175@@ -464,8 +464,12 @@
1176 tracks = meeting.tracks.all()
1177 user_is_attending = attendee is not None and attendee in meeting.attendees
1178 user_is_participating = attendee is not None and attendee in [p.attendee for p in participants if not p.from_launchpad]
1179-
1180- if attendee == meeting.drafter:
1181+ required = False
1182+
1183+ if attendee is not None and attendee in meeting.required_attendees:
1184+ required = True
1185+
1186+ if attendee is not None and attendee == meeting.drafter:
1187 drafter = True
1188 else:
1189 drafter = False
1190@@ -478,6 +482,7 @@
1191 'attendees': attendees,
1192 'user_is_attending': user_is_attending,
1193 'user_is_participating': user_is_participating,
1194+ 'required': required,
1195 'tracks': tracks,
1196 'ETHERPAD_HOST': summit.etherpad,
1197 'summit_organizer': summit.is_organizer(attendee),
1198@@ -661,8 +666,11 @@
1199 context = {
1200 'past_summit': pastsummit,
1201 }
1202- return render_to_response("schedule/past_summit.html", context,
1203- context_instance=RequestContext(request))
1204+ return render_to_response(
1205+ "schedule/past_summit.html",
1206+ context,
1207+ context_instance=RequestContext(request)
1208+ )
1209
1210
1211 @summit_attendee_required
1212@@ -695,8 +703,11 @@
1213 'summit': summit,
1214 'form': form,
1215 }
1216- return render_to_response('schedule/create_meeting.html',
1217- context, RequestContext(request))
1218+ return render_to_response(
1219+ 'schedule/create_meeting.html',
1220+ context,
1221+ RequestContext(request)
1222+ )
1223
1224
1225 @summit_attendee_required
1226@@ -721,8 +732,11 @@
1227 'summit': summit,
1228 'form': form,
1229 }
1230- return render_to_response('schedule/propose_meeting.html',
1231- context, RequestContext(request))
1232+ return render_to_response(
1233+ 'schedule/propose_meeting.html',
1234+ context,
1235+ RequestContext(request)
1236+ )
1237
1238
1239 @summit_attendee_required
1240@@ -755,8 +769,11 @@
1241 'summit': summit,
1242 'form': form,
1243 }
1244- return render_to_response('schedule/org_edit_meeting.html',
1245- context, RequestContext(request))
1246+ return render_to_response(
1247+ 'schedule/org_edit_meeting.html',
1248+ context,
1249+ RequestContext(request)
1250+ )
1251
1252
1253 @summit_attendee_required
1254@@ -783,8 +800,11 @@
1255 'summit': summit,
1256 'form': form,
1257 }
1258- return render_to_response('schedule/edit_meeting.html',
1259- context, RequestContext(request))
1260+ return render_to_response(
1261+ 'schedule/edit_meeting.html',
1262+ context,
1263+ RequestContext(request)
1264+ )
1265
1266
1267 @summit_attendee_required
1268@@ -818,8 +838,11 @@
1269 'summit': summit,
1270 'form': form,
1271 }
1272- return render_to_response('schedule/edit_hangout.html',
1273- context, RequestContext(request))
1274+ return render_to_response(
1275+ 'schedule/edit_hangout.html',
1276+ context,
1277+ RequestContext(request)
1278+ )
1279
1280
1281 @summit_required
1282@@ -877,8 +900,11 @@
1283 'summit': summit,
1284 'form': form,
1285 }
1286- return render_to_response('schedule/meeting_review.html',
1287- context, RequestContext(request))
1288+ return render_to_response(
1289+ 'schedule/meeting_review.html',
1290+ context,
1291+ RequestContext(request)
1292+ )
1293
1294
1295 @summit_required
1296@@ -902,8 +928,11 @@
1297 'drafter': drafter,
1298 'attendee': attendee,
1299 }
1300- return render_to_response("schedule/mine.html", context,
1301- context_instance=RequestContext(request))
1302+ return render_to_response(
1303+ "schedule/mine.html",
1304+ context,
1305+ context_instance=RequestContext(request)
1306+ )
1307
1308
1309 @summit_required
1310@@ -968,8 +997,11 @@
1311 'meeting.title': meeting.title,
1312 'meeting': meeting,
1313 }
1314- return render_to_response('schedule/attend.html',
1315- context, RequestContext(request))
1316+ return render_to_response(
1317+ 'schedule/attend.html',
1318+ context,
1319+ RequestContext(request)
1320+ )
1321
1322
1323 @summit_required
1324@@ -1054,8 +1086,11 @@
1325 'meeting': meeting,
1326 'attendee': attendee,
1327 }
1328- return render_to_response('schedule/org_attend.html',
1329- context, RequestContext(request))
1330+ return render_to_response(
1331+ 'schedule/org_attend.html',
1332+ context,
1333+ RequestContext(request)
1334+ )
1335
1336
1337 @summit_attendee_required
1338@@ -1081,8 +1116,11 @@
1339 'meeting.title': meeting.title,
1340 'meeting': meeting,
1341 }
1342- return render_to_response('schedule/delete_confirm.html',
1343- context, RequestContext(request))
1344+ return render_to_response(
1345+ 'schedule/delete_confirm.html',
1346+ context,
1347+ RequestContext(request)
1348+ )
1349
1350
1351 @summit_attendee_required
1352@@ -1114,5 +1152,8 @@
1353 'summit': summit
1354 }
1355
1356- return render_to_response('schedule/confirm_deletion.html',
1357- context, RequestContext(request))
1358+ return render_to_response(
1359+ 'schedule/confirm_deletion.html',
1360+ context,
1361+ RequestContext(request)
1362+ )

Subscribers

People subscribed via source and target branches