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
=== modified file 'summit/schedule/admin/meetingadmin.py'
--- summit/schedule/admin/meetingadmin.py 2011-05-31 10:43:35 +0000
+++ summit/schedule/admin/meetingadmin.py 2011-09-15 20:51:24 +0000
@@ -154,7 +154,7 @@
154 fieldsets = (154 fieldsets = (
155 (None, {155 (None, {
156 'fields': ('summit', 'name', 'title', 'description',156 'fields': ('summit', 'name', 'title', 'description',
157 'type', 'tracks', 'topics'),157 'type', 'tracks', 'topics', 'requires_dial_in'),
158 }),158 }),
159 ("References", {159 ("References", {
160 'fields': ('spec_url', 'wiki_url', 'pad_url'),160 'fields': ('spec_url', 'wiki_url', 'pad_url'),
161161
=== modified file 'summit/schedule/admin/roomadmin.py'
--- summit/schedule/admin/roomadmin.py 2010-03-05 10:33:36 +0000
+++ summit/schedule/admin/roomadmin.py 2011-09-15 20:51:24 +0000
@@ -36,7 +36,7 @@
36 fieldsets = (36 fieldsets = (
37 (None, {37 (None, {
38 'fields': ('summit', 'name', 'title', 'type', 'size', 'tracks',38 'fields': ('summit', 'name', 'title', 'type', 'size', 'tracks',
39 'icecast_url'),39 'icecast_url', 'has_dial_in'),
40 }),40 }),
41 ("Availability", {41 ("Availability", {
42 'fields': ('start_utc', 'end_utc'),42 'fields': ('start_utc', 'end_utc'),
4343
=== added file 'summit/schedule/migrations/0004_add_dial_in_fields.py'
--- summit/schedule/migrations/0004_add_dial_in_fields.py 1970-01-01 00:00:00 +0000
+++ summit/schedule/migrations/0004_add_dial_in_fields.py 2011-09-15 20:51:24 +0000
@@ -0,0 +1,193 @@
1# encoding: utf-8
2import datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7class Migration(SchemaMigration):
8
9 def forwards(self, orm):
10
11 # Adding field 'Meeting.requires_dial_in'
12 db.add_column('schedule_meeting', 'requires_dial_in', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
13
14 # Adding field 'Room.has_dial_in'
15 db.add_column('schedule_room', 'has_dial_in', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
16
17
18 def backwards(self, orm):
19
20 # Deleting field 'Meeting.requires_dial_in'
21 db.delete_column('schedule_meeting', 'requires_dial_in')
22
23 # Deleting field 'Room.has_dial_in'
24 db.delete_column('schedule_room', 'has_dial_in')
25
26
27 models = {
28 'auth.group': {
29 'Meta': {'object_name': 'Group'},
30 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
31 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
32 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
33 },
34 'auth.permission': {
35 'Meta': {'ordering': "('content_type__app_label', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
36 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
37 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
38 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
39 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
40 },
41 'auth.user': {
42 'Meta': {'object_name': 'User'},
43 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
44 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
45 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
46 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
47 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
48 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
49 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
50 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
51 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
52 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
53 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
54 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
55 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
56 },
57 'contenttypes.contenttype': {
58 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
59 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
60 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
61 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
62 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
63 },
64 'schedule.agenda': {
65 'Meta': {'ordering': "('slot', 'room')", 'unique_together': "(('slot', 'room'),)", 'object_name': 'Agenda'},
66 'auto': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
67 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
68 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Meeting']"}),
69 'room': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Room']"}),
70 'slot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Slot']"})
71 },
72 'schedule.attendee': {
73 'Meta': {'ordering': "('summit', 'user')", 'object_name': 'Attendee'},
74 'crew': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_column': "'crew'"}),
75 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}),
76 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
77 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"}),
78 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
79 'topics': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Topic']", 'symmetrical': 'False', 'blank': 'True'}),
80 'tracks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Track']", 'symmetrical': 'False', 'blank': 'True'}),
81 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
82 },
83 'schedule.attendeebusy': {
84 'Meta': {'ordering': "('attendee', 'start_utc', 'end_utc')", 'object_name': 'AttendeeBusy'},
85 'attendee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'busy_set'", 'to': "orm['schedule.Attendee']"}),
86 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}),
87 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
88 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"})
89 },
90 'schedule.crew': {
91 'Meta': {'ordering': "('date_utc', 'attendee')", 'object_name': 'Crew'},
92 'attendee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'crew_schedule'", 'to': "orm['schedule.Attendee']"}),
93 'date_utc': ('django.db.models.fields.DateField', [], {'db_column': "'date'"}),
94 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
95 },
96 'schedule.meeting': {
97 'Meta': {'object_name': 'Meeting'},
98 'approver': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'approver_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
99 'assignee': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'assignee_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
100 'description': ('django.db.models.fields.TextField', [], {'max_length': '2047', 'blank': 'True'}),
101 'drafter': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'drafter_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
102 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
103 'name': ('summit.schedule.fields.NameField', [], {'max_length': '50', 'blank': 'True'}),
104 'pad_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
105 'participants': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Attendee']", 'symmetrical': 'False', 'through': "'Participant'", 'blank': 'True'}),
106 'priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
107 'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
108 'requires_dial_in': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
109 'scribe': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'scribe_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
110 'slots': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
111 'spec_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
112 'status': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
113 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
114 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
115 'topics': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Topic']", 'symmetrical': 'False', 'blank': 'True'}),
116 'tracks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Track']", 'symmetrical': 'False', 'blank': 'True'}),
117 'type': ('django.db.models.fields.CharField', [], {'default': "u'blueprint'", 'max_length': '15'}),
118 'videographer1': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'videographer1_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
119 'videographer2': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'videographer2_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}),
120 'wiki_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
121 },
122 'schedule.participant': {
123 'Meta': {'object_name': 'Participant'},
124 'attendee': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Attendee']"}),
125 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
126 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Meeting']"}),
127 'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
128 },
129 'schedule.room': {
130 'Meta': {'ordering': "('summit', 'name')", 'object_name': 'Room'},
131 'end_utc': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_column': "'end'", 'blank': 'True'}),
132 'has_dial_in': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
133 'icecast_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
134 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
135 'name': ('summit.schedule.fields.NameField', [], {'max_length': '50'}),
136 'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
137 'start_utc': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_column': "'start'", 'blank': 'True'}),
138 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
139 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
140 'tracks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Track']", 'symmetrical': 'False', 'blank': 'True'}),
141 'type': ('django.db.models.fields.CharField', [], {'default': "u'open'", 'max_length': '7'})
142 },
143 'schedule.roombusy': {
144 'Meta': {'ordering': "('room', 'start_utc', 'end_utc')", 'object_name': 'RoomBusy'},
145 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}),
146 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
147 'room': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'busy_set'", 'to': "orm['schedule.Room']"}),
148 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"})
149 },
150 'schedule.slot': {
151 'Meta': {'ordering': "('summit', 'start_utc', 'end_utc')", 'object_name': 'Slot'},
152 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}),
153 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
154 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"}),
155 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
156 'type': ('django.db.models.fields.CharField', [], {'default': "u'open'", 'max_length': '7'})
157 },
158 'schedule.summit': {
159 'Meta': {'ordering': "('name',)", 'object_name': 'Summit'},
160 'date_end': ('django.db.models.fields.DateField', [], {'null': 'True'}),
161 'date_start': ('django.db.models.fields.DateField', [], {'null': 'True'}),
162 'description': ('django.db.models.fields.TextField', [], {'max_length': '2047', 'blank': 'True'}),
163 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
164 'last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
165 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
166 'name': ('summit.schedule.fields.NameField', [], {'max_length': '50'}),
167 'state': ('django.db.models.fields.CharField', [], {'default': "u'sponsor'", 'max_length': '10'}),
168 'timezone': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
169 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
170 },
171 'schedule.summitsprint': {
172 'Meta': {'ordering': "('summit', 'import_url')", 'object_name': 'SummitSprint'},
173 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
174 'import_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
175 'summit': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sprint_set'", 'to': "orm['schedule.Summit']"})
176 },
177 'schedule.topic': {
178 'Meta': {'ordering': "('summit', 'title')", 'object_name': 'Topic'},
179 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
180 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
181 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
182 },
183 'schedule.track': {
184 'Meta': {'ordering': "('summit', 'title', 'slug')", 'object_name': 'Track'},
185 'color': ('django.db.models.fields.CharField', [], {'default': "'FFFFFF'", 'max_length': '6'}),
186 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
187 'slug': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
188 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}),
189 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
190 }
191 }
192
193 complete_apps = ['schedule']
0194
=== modified file 'summit/schedule/models/meetingmodel.py'
--- summit/schedule/models/meetingmodel.py 2011-09-14 01:00:37 +0000
+++ summit/schedule/models/meetingmodel.py 2011-09-15 20:51:24 +0000
@@ -90,6 +90,7 @@
90 verbose_name="Pad URL", null=True, blank=True)90 verbose_name="Pad URL", null=True, blank=True)
91 slots = models.IntegerField(default=1)91 slots = models.IntegerField(default=1)
92 private = models.BooleanField(default=False)92 private = models.BooleanField(default=False)
93 requires_dial_in = models.BooleanField(default=False)
93 # FIXME attendees must be for the same summit94 # FIXME attendees must be for the same summit
94 # (will require js magic in admin to refresh the boxes)95 # (will require js magic in admin to refresh the boxes)
95 drafter = models.ForeignKey(Attendee, null=True, blank=True,96 drafter = models.ForeignKey(Attendee, null=True, blank=True,
@@ -343,6 +344,10 @@
343 if not room.available(this_slot):344 if not room.available(this_slot):
344 raise Meeting.SchedulingError("Room is not available")345 raise Meeting.SchedulingError("Room is not available")
345346
347 if self.requires_dial_in:
348 if not room.has_dial_in:
349 raise Meeting.SchedulingError("Room has no dial-in capability")
350
346 # Work out who is busy in this slot351 # Work out who is busy in this slot
347 busy = set()352 busy = set()
348 for agenda in this_slot.agenda_set.all():353 for agenda in this_slot.agenda_set.all():
349354
=== modified file 'summit/schedule/models/roommodel.py'
--- summit/schedule/models/roommodel.py 2011-05-15 01:11:24 +0000
+++ summit/schedule/models/roommodel.py 2011-09-15 20:51:24 +0000
@@ -51,6 +51,10 @@
51 verbose_name="End (UTC)")51 verbose_name="End (UTC)")
52 icecast_url = models.URLField(blank=True, verify_exists=False,52 icecast_url = models.URLField(blank=True, verify_exists=False,
53 verbose_name="Icecast URL")53 verbose_name="Icecast URL")
54
55 # Whether the room has dial-in capability
56 has_dial_in = models.BooleanField(default=False)
57
54 # people who cannot be scheduled here58 # people who cannot be scheduled here
55 # voip, listen-only and icecast urls59 # voip, listen-only and icecast urls
56 class Meta:60 class Meta:
5761
=== modified file 'summit/schedule/tests.py'
--- summit/schedule/tests.py 2011-09-15 17:22:40 +0000
+++ summit/schedule/tests.py 2011-09-15 20:51:24 +0000
@@ -66,6 +66,44 @@
66 Meeting.SchedulingError,66 Meeting.SchedulingError,
67 meeting.check_schedule, agenda.slot, agenda.room)67 meeting.check_schedule, agenda.slot, agenda.room)
6868
69 def make_open_slot(self):
70 now = datetime.datetime.utcnow()
71 one_hour = datetime.timedelta(0, 3600)
72 slot = factory.make_one(
73 Slot,
74 start_utc=now+one_hour,
75 end_utc=now+one_hour+one_hour,
76 type='open')
77 return slot
78
79 def test_check_schedule_errors_on_no_dial_in(self):
80 slot = self.make_open_slot()
81 room = factory.make_one(Room, has_dial_in=False, summit=slot.summit, name="testroom")
82 meeting = factory.make_one(Meeting, requires_dial_in=True, summit=slot.summit, name="testmeeting")
83 try:
84 meeting.check_schedule(slot, room)
85 except meeting.SchedulingError, e:
86 print e
87 self.assertEqual("Room has no dial-in capability", e.message)
88 return
89 self.fail("SchedulingError not thrown")
90
91 def test_try_schedule_into_refuses_room_without_dial_in(self):
92 slot = self.make_open_slot()
93 room = factory.make_one(Room, has_dial_in=False, summit=slot.summit, name="testroom")
94 meeting = factory.make_one(Meeting, requires_dial_in=True, summit=slot.summit, name="testmeeting")
95
96 self.assertEqual(False, meeting.try_schedule_into([room]))
97 self.assertEqual(0, meeting.agenda_set.all().count())
98
99 def test_try_schedule_into_allows_room_with_dial_in(self):
100 slot = self.make_open_slot()
101 room = factory.make_one(Room, has_dial_in=True, summit=slot.summit, name="testroom")
102 meeting = factory.make_one(Meeting, requires_dial_in=True, summit=slot.summit, name="testmeeting")
103
104 self.assertEqual(True, meeting.try_schedule_into([room]))
105 self.assertEqual(1, meeting.agenda_set.all().count())
106
69107
70class ICalTestCase(djangotest.TestCase):108class ICalTestCase(djangotest.TestCase):
71109

Subscribers

People subscribed via source and target branches