Merge lp:~james-w/summit/dial-in into lp:summit

Proposed by James Westby
Status: Merged
Approved by: Michael Hall
Approved revision: 171
Merged at revision: 174
Proposed branch: lp:~james-w/summit/dial-in
Merge into: lp:summit
Diff against target: 309 lines (+242/-2)
6 files modified
summit/schedule/admin/meetingadmin.py (+1/-1)
summit/schedule/admin/roomadmin.py (+1/-1)
summit/schedule/migrations/0004_add_dial_in_fields.py (+193/-0)
summit/schedule/models/meetingmodel.py (+5/-0)
summit/schedule/models/roommodel.py (+4/-0)
summit/schedule/tests.py (+38/-0)
To merge this branch: bzr merge lp:~james-w/summit/dial-in
Reviewer Review Type Date Requested Status
Michael Hall (community) Approve
Review via email: mp+75631@code.launchpad.net

Commit message

Add fields on rooms and meetings for dial-in.

A meeting that requires dial-in can only be scheduled in to a room
that has it.

Description of the change

Hi,

This is a surprisingly small branch that adds dial-in knowledge
to meetings and rooms.

A meeting can specify that it requires dial-in, and such meetings
can only be scheduled in to rooms that have dial-in.

It will be up to admins to manage these fields, you can't put
something in the LP blueprint for instance.

My next branch will update the admin interface to show these
fields.

Thanks,

James

To post a comment you must log in.
Revision history for this message
James Westby (james-w) wrote :

Well, the admin site was easier than I thought, so I've pushed
that now too.

lp:~james-w/summit/dial-in updated
171. By James Westby

Add the dial-in fields to the admin site.

Revision history for this message
James Westby (james-w) wrote :

Hi,

Michael asked if we should be showing dial-in information on the schedule.

I talked to Joey about it, and he's not sure.

I propose that we land this so that the scheduling can be done, and we can
land something to store and display that information if it is needed, once
we know how it should work.

At worst the session lead could put the information in the etherpad for the
session.

Thanks,

James

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

Looks good

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'summit/schedule/admin/meetingadmin.py'
2--- summit/schedule/admin/meetingadmin.py 2011-05-31 10:43:35 +0000
3+++ summit/schedule/admin/meetingadmin.py 2011-09-15 20:51:24 +0000
4@@ -154,7 +154,7 @@
5 fieldsets = (
6 (None, {
7 'fields': ('summit', 'name', 'title', 'description',
8- 'type', 'tracks', 'topics'),
9+ 'type', 'tracks', 'topics', 'requires_dial_in'),
10 }),
11 ("References", {
12 'fields': ('spec_url', 'wiki_url', 'pad_url'),
13
14=== modified file 'summit/schedule/admin/roomadmin.py'
15--- summit/schedule/admin/roomadmin.py 2010-03-05 10:33:36 +0000
16+++ summit/schedule/admin/roomadmin.py 2011-09-15 20:51:24 +0000
17@@ -36,7 +36,7 @@
18 fieldsets = (
19 (None, {
20 'fields': ('summit', 'name', 'title', 'type', 'size', 'tracks',
21- 'icecast_url'),
22+ 'icecast_url', 'has_dial_in'),
23 }),
24 ("Availability", {
25 'fields': ('start_utc', 'end_utc'),
26
27=== added file 'summit/schedule/migrations/0004_add_dial_in_fields.py'
28--- summit/schedule/migrations/0004_add_dial_in_fields.py 1970-01-01 00:00:00 +0000
29+++ summit/schedule/migrations/0004_add_dial_in_fields.py 2011-09-15 20:51:24 +0000
30@@ -0,0 +1,193 @@
31+# encoding: utf-8
32+import datetime
33+from south.db import db
34+from south.v2 import SchemaMigration
35+from django.db import models
36+
37+class Migration(SchemaMigration):
38+
39+ def forwards(self, orm):
40+
41+ # Adding field 'Meeting.requires_dial_in'
42+ db.add_column('schedule_meeting', 'requires_dial_in', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
43+
44+ # Adding field 'Room.has_dial_in'
45+ db.add_column('schedule_room', 'has_dial_in', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
46+
47+
48+ def backwards(self, orm):
49+
50+ # Deleting field 'Meeting.requires_dial_in'
51+ db.delete_column('schedule_meeting', 'requires_dial_in')
52+
53+ # Deleting field 'Room.has_dial_in'
54+ db.delete_column('schedule_room', 'has_dial_in')
55+
56+
57+ models = {
58+ 'auth.group': {
59+ 'Meta': {'object_name': 'Group'},
60+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
61+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
62+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
63+ },
64+ 'auth.permission': {
65+ 'Meta': {'ordering': "('content_type__app_label', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
66+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
67+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
68+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
69+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
70+ },
71+ 'auth.user': {
72+ 'Meta': {'object_name': 'User'},
73+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
74+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
75+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
76+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
77+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
78+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
79+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
80+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
81+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
82+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
83+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
84+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
85+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
86+ },
87+ 'contenttypes.contenttype': {
88+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
89+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
90+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
91+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
92+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
93+ },
94+ 'schedule.agenda': {
95+ 'Meta': {'ordering': "('slot', 'room')", 'unique_together': "(('slot', 'room'),)", 'object_name': 'Agenda'},
96+ 'auto': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
97+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
98+ 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Meeting']"}),
99+ 'room': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Room']"}),
100+ 'slot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Slot']"})
101+ },
102+ 'schedule.attendee': {
103+ 'Meta': {'ordering': "('summit', 'user')", 'object_name': 'Attendee'},
104+ 'crew': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_column': "'crew'"}),
105+ 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}),
106+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
107+ 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"}),
108+ 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
109+ 'topics': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Topic']", 'symmetrical': 'False', 'blank': 'True'}),
110+ 'tracks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Track']", 'symmetrical': 'False', 'blank': 'True'}),
111+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
112+ },
113+ 'schedule.attendeebusy': {
114+ 'Meta': {'ordering': "('attendee', 'start_utc', 'end_utc')", 'object_name': 'AttendeeBusy'},
115+ 'attendee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'busy_set'", 'to': "orm['schedule.Attendee']"}),
116+ 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}),
117+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
118+ 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"})
119+ },
120+ 'schedule.crew': {
121+ 'Meta': {'ordering': "('date_utc', 'attendee')", 'object_name': 'Crew'},
122+ 'attendee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'crew_schedule'", 'to': "orm['schedule.Attendee']"}),
123+ 'date_utc': ('django.db.models.fields.DateField', [], {'db_column': "'date'"}),
124+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
125+ },
126+ 'schedule.meeting': {
127+ 'Meta': {'object_name': 'Meeting'},
128+ 'approver': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'approver_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
129+ 'assignee': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'assignee_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
130+ 'description': ('django.db.models.fields.TextField', [], {'max_length': '2047', 'blank': 'True'}),
131+ 'drafter': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'drafter_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
132+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
133+ 'name': ('summit.schedule.fields.NameField', [], {'max_length': '50', 'blank': 'True'}),
134+ 'pad_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
135+ 'participants': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Attendee']", 'symmetrical': 'False', 'through': "'Participant'", 'blank': 'True'}),
136+ 'priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
137+ 'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
138+ 'requires_dial_in': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
139+ 'scribe': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'scribe_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
140+ 'slots': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
141+ 'spec_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
142+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
143+ 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
144+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
145+ 'topics': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Topic']", 'symmetrical': 'False', 'blank': 'True'}),
146+ 'tracks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Track']", 'symmetrical': 'False', 'blank': 'True'}),
147+ 'type': ('django.db.models.fields.CharField', [], {'default': "u'blueprint'", 'max_length': '15'}),
148+ 'videographer1': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'videographer1_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
149+ 'videographer2': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'videographer2_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
150+ 'wiki_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
151+ },
152+ 'schedule.participant': {
153+ 'Meta': {'object_name': 'Participant'},
154+ 'attendee': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Attendee']"}),
155+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
156+ 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Meeting']"}),
157+ 'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
158+ },
159+ 'schedule.room': {
160+ 'Meta': {'ordering': "('summit', 'name')", 'object_name': 'Room'},
161+ 'end_utc': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_column': "'end'", 'blank': 'True'}),
162+ 'has_dial_in': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
163+ 'icecast_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
164+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
165+ 'name': ('summit.schedule.fields.NameField', [], {'max_length': '50'}),
166+ 'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
167+ 'start_utc': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_column': "'start'", 'blank': 'True'}),
168+ 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
169+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
170+ 'tracks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Track']", 'symmetrical': 'False', 'blank': 'True'}),
171+ 'type': ('django.db.models.fields.CharField', [], {'default': "u'open'", 'max_length': '7'})
172+ },
173+ 'schedule.roombusy': {
174+ 'Meta': {'ordering': "('room', 'start_utc', 'end_utc')", 'object_name': 'RoomBusy'},
175+ 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}),
176+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
177+ 'room': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'busy_set'", 'to': "orm['schedule.Room']"}),
178+ 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"})
179+ },
180+ 'schedule.slot': {
181+ 'Meta': {'ordering': "('summit', 'start_utc', 'end_utc')", 'object_name': 'Slot'},
182+ 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}),
183+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
184+ 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"}),
185+ 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
186+ 'type': ('django.db.models.fields.CharField', [], {'default': "u'open'", 'max_length': '7'})
187+ },
188+ 'schedule.summit': {
189+ 'Meta': {'ordering': "('name',)", 'object_name': 'Summit'},
190+ 'date_end': ('django.db.models.fields.DateField', [], {'null': 'True'}),
191+ 'date_start': ('django.db.models.fields.DateField', [], {'null': 'True'}),
192+ 'description': ('django.db.models.fields.TextField', [], {'max_length': '2047', 'blank': 'True'}),
193+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
194+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
195+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
196+ 'name': ('summit.schedule.fields.NameField', [], {'max_length': '50'}),
197+ 'state': ('django.db.models.fields.CharField', [], {'default': "u'sponsor'", 'max_length': '10'}),
198+ 'timezone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
199+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
200+ },
201+ 'schedule.summitsprint': {
202+ 'Meta': {'ordering': "('summit', 'import_url')", 'object_name': 'SummitSprint'},
203+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
204+ 'import_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
205+ 'summit': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sprint_set'", 'to': "orm['schedule.Summit']"})
206+ },
207+ 'schedule.topic': {
208+ 'Meta': {'ordering': "('summit', 'title')", 'object_name': 'Topic'},
209+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
210+ 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
211+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
212+ },
213+ 'schedule.track': {
214+ 'Meta': {'ordering': "('summit', 'title', 'slug')", 'object_name': 'Track'},
215+ 'color': ('django.db.models.fields.CharField', [], {'default': "'FFFFFF'", 'max_length': '6'}),
216+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
217+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
218+ 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
219+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
220+ }
221+ }
222+
223+ complete_apps = ['schedule']
224
225=== modified file 'summit/schedule/models/meetingmodel.py'
226--- summit/schedule/models/meetingmodel.py 2011-09-14 01:00:37 +0000
227+++ summit/schedule/models/meetingmodel.py 2011-09-15 20:51:24 +0000
228@@ -90,6 +90,7 @@
229 verbose_name="Pad URL", null=True, blank=True)
230 slots = models.IntegerField(default=1)
231 private = models.BooleanField(default=False)
232+ requires_dial_in = models.BooleanField(default=False)
233 # FIXME attendees must be for the same summit
234 # (will require js magic in admin to refresh the boxes)
235 drafter = models.ForeignKey(Attendee, null=True, blank=True,
236@@ -343,6 +344,10 @@
237 if not room.available(this_slot):
238 raise Meeting.SchedulingError("Room is not available")
239
240+ if self.requires_dial_in:
241+ if not room.has_dial_in:
242+ raise Meeting.SchedulingError("Room has no dial-in capability")
243+
244 # Work out who is busy in this slot
245 busy = set()
246 for agenda in this_slot.agenda_set.all():
247
248=== modified file 'summit/schedule/models/roommodel.py'
249--- summit/schedule/models/roommodel.py 2011-05-15 01:11:24 +0000
250+++ summit/schedule/models/roommodel.py 2011-09-15 20:51:24 +0000
251@@ -51,6 +51,10 @@
252 verbose_name="End (UTC)")
253 icecast_url = models.URLField(blank=True, verify_exists=False,
254 verbose_name="Icecast URL")
255+
256+ # Whether the room has dial-in capability
257+ has_dial_in = models.BooleanField(default=False)
258+
259 # people who cannot be scheduled here
260 # voip, listen-only and icecast urls
261 class Meta:
262
263=== modified file 'summit/schedule/tests.py'
264--- summit/schedule/tests.py 2011-09-15 17:22:40 +0000
265+++ summit/schedule/tests.py 2011-09-15 20:51:24 +0000
266@@ -66,6 +66,44 @@
267 Meeting.SchedulingError,
268 meeting.check_schedule, agenda.slot, agenda.room)
269
270+ def make_open_slot(self):
271+ now = datetime.datetime.utcnow()
272+ one_hour = datetime.timedelta(0, 3600)
273+ slot = factory.make_one(
274+ Slot,
275+ start_utc=now+one_hour,
276+ end_utc=now+one_hour+one_hour,
277+ type='open')
278+ return slot
279+
280+ def test_check_schedule_errors_on_no_dial_in(self):
281+ slot = self.make_open_slot()
282+ room = factory.make_one(Room, has_dial_in=False, summit=slot.summit, name="testroom")
283+ meeting = factory.make_one(Meeting, requires_dial_in=True, summit=slot.summit, name="testmeeting")
284+ try:
285+ meeting.check_schedule(slot, room)
286+ except meeting.SchedulingError, e:
287+ print e
288+ self.assertEqual("Room has no dial-in capability", e.message)
289+ return
290+ self.fail("SchedulingError not thrown")
291+
292+ def test_try_schedule_into_refuses_room_without_dial_in(self):
293+ slot = self.make_open_slot()
294+ room = factory.make_one(Room, has_dial_in=False, summit=slot.summit, name="testroom")
295+ meeting = factory.make_one(Meeting, requires_dial_in=True, summit=slot.summit, name="testmeeting")
296+
297+ self.assertEqual(False, meeting.try_schedule_into([room]))
298+ self.assertEqual(0, meeting.agenda_set.all().count())
299+
300+ def test_try_schedule_into_allows_room_with_dial_in(self):
301+ slot = self.make_open_slot()
302+ room = factory.make_one(Room, has_dial_in=True, summit=slot.summit, name="testroom")
303+ meeting = factory.make_one(Meeting, requires_dial_in=True, summit=slot.summit, name="testmeeting")
304+
305+ self.assertEqual(True, meeting.try_schedule_into([room]))
306+ self.assertEqual(1, meeting.agenda_set.all().count())
307+
308
309 class ICalTestCase(djangotest.TestCase):
310

Subscribers

People subscribed via source and target branches