Merge lp:~mhall119/summit/support-short-slots into lp:summit
- support-short-slots
- Merge into trunk
| Status: | Merged |
|---|---|
| Approved by: | Chris Johnston on 2012-10-04 |
| Approved revision: | 455 |
| Merged at revision: | 451 |
| Proposed branch: | lp:~mhall119/summit/support-short-slots |
| Merge into: | lp:summit |
| Diff against target: |
559 lines (+309/-31) 7 files modified
summit/common/management/commands/lpupdate.py (+11/-2) summit/schedule/admin/meetingadmin.py (+1/-1) summit/schedule/management/commands/initslots.py (+14/-6) summit/schedule/migrations/0025_add_meeting_blueprint_id.py (+207/-0) summit/schedule/models/meetingmodel.py (+8/-0) summit/schedule/models/summitmodel.py (+33/-14) summit/schedule/tests.py (+35/-8) |
| To merge this branch: | bzr merge lp:~mhall119/summit/support-short-slots |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Chris Johnston | 2012-10-02 | Approve on 2012-10-04 | |
|
Review via email:
|
|||
Commit Message
Adds the ability to support 30-minute slots in both initslots and lpupdate commands, even though we won't be using them for UDS-R.
Also adds support for LP Blueprint IDs, and using them to match instead of BP name, so that renames are handled properly, and removed BPs are marked as "REMOVED" but not deleted.
Description of the Change
Adds the ability to support 30-minute slots in both initslots and lpupdate commands, even though we won't be using them for UDS-R.
Also adds support for LP Blueprint IDs, and using them to match instead of BP name, so that renames are handled properly, and removed BPs are marked as "REMOVED" but not deleted.
| Chris Johnston (cjohnston) wrote : | # |
| Michael Hall (mhall119) wrote : | # |
it's not deleting, that's the old comment text, it's just marking them as REMOVED so they won't be scheduled
- 453. By Michael Hall on 2012-10-04
-
Change comment about deleting meetings
- 454. By Michael Hall on 2012-10-04
-
Update comment
- 455. By Michael Hall on 2012-10-04
-
only mark meetings removed when importing meetings
Preview Diff
| 1 | === modified file 'summit/common/management/commands/lpupdate.py' |
| 2 | --- summit/common/management/commands/lpupdate.py 2012-02-25 00:13:45 +0000 |
| 3 | +++ summit/common/management/commands/lpupdate.py 2012-10-04 13:59:28 +0000 |
| 4 | @@ -17,6 +17,7 @@ |
| 5 | from django.core.management.base import BaseCommand, CommandError |
| 6 | |
| 7 | from summit.schedule.models import Summit |
| 8 | +from optparse import make_option |
| 9 | |
| 10 | __all__ = ( |
| 11 | 'Command', |
| 12 | @@ -24,16 +25,24 @@ |
| 13 | |
| 14 | |
| 15 | class Command(BaseCommand): |
| 16 | + option_list = BaseCommand.option_list + ( |
| 17 | + make_option("-s", "--slots", dest="slots", |
| 18 | + help="Number of slots an imported meeting needs", type=int, default=1), |
| 19 | + make_option("-A", "--no-attendees", dest="skip_attendees", action="store_true", |
| 20 | + help="Don't import Attendee data", default=False), |
| 21 | + make_option("-M", "--no-meetings", dest="skip_meetings", action="store_true", |
| 22 | + help="Don't import Meeting data", default=False), |
| 23 | + ) |
| 24 | |
| 25 | def handle(self, summit='', *args, **options): |
| 26 | if not summit: |
| 27 | for summit in Summit.objects.all(): |
| 28 | print "Updating %s" % summit |
| 29 | - summit.update_from_launchpad() |
| 30 | + summit.update_from_launchpad(options) |
| 31 | else: |
| 32 | try: |
| 33 | summit = Summit.objects.get(name=summit) |
| 34 | except Summit.DoesNotExist: |
| 35 | raise CommandError("Summit doesn't exist: %s" % summit) |
| 36 | print "Updating %s" % summit |
| 37 | - summit.update_from_launchpad() |
| 38 | + summit.update_from_launchpad(options) |
| 39 | |
| 40 | === modified file 'summit/schedule/admin/meetingadmin.py' |
| 41 | --- summit/schedule/admin/meetingadmin.py 2012-05-16 14:59:59 +0000 |
| 42 | +++ summit/schedule/admin/meetingadmin.py 2012-10-04 13:59:28 +0000 |
| 43 | @@ -144,7 +144,7 @@ |
| 44 | 'type', 'tracks', 'requires_dial_in', 'video'), |
| 45 | }), |
| 46 | ("References", { |
| 47 | - 'fields': ('spec_url', 'wiki_url', 'pad_url'), |
| 48 | + 'fields': ('spec_url', 'launchpad_blueprint_id', 'wiki_url', 'pad_url'), |
| 49 | }), |
| 50 | ("Scheduling details", { |
| 51 | 'classes': ('collapse', ), |
| 52 | |
| 53 | === modified file 'summit/schedule/management/commands/initslots.py' |
| 54 | --- summit/schedule/management/commands/initslots.py 2012-03-08 22:19:17 +0000 |
| 55 | +++ summit/schedule/management/commands/initslots.py 2012-10-04 13:59:28 +0000 |
| 56 | @@ -58,6 +58,9 @@ |
| 57 | duration = datetime.timedelta(minutes=options["duration"]) |
| 58 | interval = datetime.timedelta(minutes=options["interval"]) |
| 59 | breaktime = datetime.timedelta(minutes=15) |
| 60 | + |
| 61 | + slots_per_hour = (60 * 60) / duration.seconds |
| 62 | + |
| 63 | try: |
| 64 | summit = Summit.objects.get(name=summit.__str__) |
| 65 | except Summit.DoesNotExist: |
| 66 | @@ -77,14 +80,19 @@ |
| 67 | # Determines the type of the session |
| 68 | if slottime.time() == lunch: |
| 69 | slot_type = 'lunch' |
| 70 | + slot_duration = datetime.timedelta(minutes=60) |
| 71 | + slot_count += (slots_per_hour - 1) |
| 72 | elif slottime.time() == plenary: |
| 73 | slot_type = 'plenary' |
| 74 | + slot_duration = datetime.timedelta(minutes=60) |
| 75 | + slot_count += (slots_per_hour - 1) |
| 76 | else: |
| 77 | slot_type = 'open' |
| 78 | - slot_length = duration |
| 79 | + slot_duration = duration |
| 80 | + slot_length = slot_duration |
| 81 | |
| 82 | # Morning Break |
| 83 | - if slot_count == 2: |
| 84 | + if slot_count == (2 * slots_per_hour): |
| 85 | slot, created = Slot.objects.get_or_create( |
| 86 | summit=summit, |
| 87 | start_utc=start_time + slot_length - breaktime, |
| 88 | @@ -93,7 +101,7 @@ |
| 89 | ) |
| 90 | slot_length = slot_length - breaktime |
| 91 | # Afternoon Break |
| 92 | - elif slot_count == 8: |
| 93 | + elif slot_count == (7 * slots_per_hour)+1: |
| 94 | slot, created = Slot.objects.get_or_create( |
| 95 | summit=summit, |
| 96 | start_utc=start_time, |
| 97 | @@ -105,10 +113,10 @@ |
| 98 | slottime = slottime + interval |
| 99 | # Non-break intervals |
| 100 | elif slot_type == 'open': |
| 101 | - nexthour = slottime + hour |
| 102 | + nexthour = slottime + duration |
| 103 | if nexthour.time() != lunch \ |
| 104 | and nexthour.time() != plenary \ |
| 105 | - and slot_count != 7: |
| 106 | + and slot_count != (7*slots_per_hour): |
| 107 | slot_length = slot_length - interval |
| 108 | |
| 109 | end_time = start_time + slot_length |
| 110 | @@ -120,7 +128,7 @@ |
| 111 | end_utc=end_time, |
| 112 | type=slot_type) |
| 113 | |
| 114 | - slottime = slottime + duration |
| 115 | + slottime = slottime + slot_duration |
| 116 | |
| 117 | date = date + day |
| 118 | print "Initializing slots complete." |
| 119 | |
| 120 | === added file 'summit/schedule/migrations/0025_add_meeting_blueprint_id.py' |
| 121 | --- summit/schedule/migrations/0025_add_meeting_blueprint_id.py 1970-01-01 00:00:00 +0000 |
| 122 | +++ summit/schedule/migrations/0025_add_meeting_blueprint_id.py 2012-10-04 13:59:28 +0000 |
| 123 | @@ -0,0 +1,207 @@ |
| 124 | +# encoding: utf-8 |
| 125 | +import datetime |
| 126 | +from south.db import db |
| 127 | +from south.v2 import SchemaMigration |
| 128 | +from django.db import models |
| 129 | + |
| 130 | +class Migration(SchemaMigration): |
| 131 | + |
| 132 | + def forwards(self, orm): |
| 133 | + |
| 134 | + # Adding field 'Meeting.launchpad_blueprint_id' |
| 135 | + db.add_column('schedule_meeting', 'launchpad_blueprint_id', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False) |
| 136 | + |
| 137 | + |
| 138 | + def backwards(self, orm): |
| 139 | + |
| 140 | + # Deleting field 'Meeting.launchpad_blueprint_id' |
| 141 | + db.delete_column('schedule_meeting', 'launchpad_blueprint_id') |
| 142 | + |
| 143 | + |
| 144 | + models = { |
| 145 | + 'auth.group': { |
| 146 | + 'Meta': {'object_name': 'Group'}, |
| 147 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 148 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
| 149 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
| 150 | + }, |
| 151 | + 'auth.permission': { |
| 152 | + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
| 153 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
| 154 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
| 155 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 156 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
| 157 | + }, |
| 158 | + 'auth.user': { |
| 159 | + 'Meta': {'ordering': "['username']", 'object_name': 'User'}, |
| 160 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
| 161 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
| 162 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
| 163 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
| 164 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 165 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
| 166 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 167 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 168 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
| 169 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
| 170 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
| 171 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
| 172 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
| 173 | + }, |
| 174 | + 'contenttypes.contenttype': { |
| 175 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
| 176 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
| 177 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 178 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
| 179 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
| 180 | + }, |
| 181 | + 'schedule.agenda': { |
| 182 | + 'Meta': {'ordering': "('slot', 'room')", 'unique_together': "(('slot', 'room'),)", 'object_name': 'Agenda'}, |
| 183 | + 'auto': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 184 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 185 | + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Meeting']"}), |
| 186 | + 'room': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Room']"}), |
| 187 | + 'slot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Slot']"}) |
| 188 | + }, |
| 189 | + 'schedule.attendee': { |
| 190 | + 'Meta': {'ordering': "('user__username', 'summit')", 'object_name': 'Attendee'}, |
| 191 | + 'crew': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_column': "'crew'"}), |
| 192 | + 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}), |
| 193 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 194 | + 'secret_key_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
| 195 | + 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"}), |
| 196 | + 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}), |
| 197 | + 'tracks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Track']", 'symmetrical': 'False', 'blank': 'True'}), |
| 198 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) |
| 199 | + }, |
| 200 | + 'schedule.attendeebusy': { |
| 201 | + 'Meta': {'ordering': "('attendee', 'start_utc', 'end_utc')", 'object_name': 'AttendeeBusy'}, |
| 202 | + 'attendee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'busy_set'", 'to': "orm['schedule.Attendee']"}), |
| 203 | + 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}), |
| 204 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 205 | + 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"}) |
| 206 | + }, |
| 207 | + 'schedule.crew': { |
| 208 | + 'Meta': {'ordering': "('date_utc', 'attendee')", 'object_name': 'Crew'}, |
| 209 | + 'attendee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'crew_schedule'", 'to': "orm['schedule.Attendee']"}), |
| 210 | + 'date_utc': ('django.db.models.fields.DateField', [], {'db_column': "'date'"}), |
| 211 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) |
| 212 | + }, |
| 213 | + 'schedule.lead': { |
| 214 | + 'Meta': {'ordering': "('summit', 'track')", 'object_name': 'Lead'}, |
| 215 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 216 | + 'lead': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'lead'", 'to': "orm['schedule.Attendee']"}), |
| 217 | + 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}), |
| 218 | + 'track': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Track']"}) |
| 219 | + }, |
| 220 | + 'schedule.meeting': { |
| 221 | + 'Meta': {'object_name': 'Meeting'}, |
| 222 | + 'approved': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '10', 'null': 'True'}), |
| 223 | + 'approver': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'approver_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}), |
| 224 | + 'assignee': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'assignee_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}), |
| 225 | + 'description': ('django.db.models.fields.TextField', [], {'max_length': '2047', 'blank': 'True'}), |
| 226 | + 'drafter': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'drafter_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}), |
| 227 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 228 | + 'launchpad_blueprint_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
| 229 | + 'name': ('summit.schedule.fields.NameField', [], {'max_length': '100'}), |
| 230 | + 'override_break': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 231 | + 'pad_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), |
| 232 | + 'participants': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Attendee']", 'symmetrical': 'False', 'through': "orm['schedule.Participant']", 'blank': 'True'}), |
| 233 | + 'priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
| 234 | + 'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 235 | + 'private_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
| 236 | + 'requires_dial_in': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 237 | + 'scribe': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'scribe_set'", 'null': 'True', 'to': "orm['schedule.Attendee']"}), |
| 238 | + 'slots': ('django.db.models.fields.IntegerField', [], {'default': '1'}), |
| 239 | + 'spec_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), |
| 240 | + 'status': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), |
| 241 | + 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}), |
| 242 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
| 243 | + 'tracks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Track']", 'symmetrical': 'False', 'blank': 'True'}), |
| 244 | + 'type': ('django.db.models.fields.CharField', [], {'default': "u'discussion'", 'max_length': '15'}), |
| 245 | + 'video': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 246 | + 'wiki_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) |
| 247 | + }, |
| 248 | + 'schedule.participant': { |
| 249 | + 'Meta': {'ordering': "('meeting', 'attendee', 'participation')", 'object_name': 'Participant'}, |
| 250 | + 'attendee': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Attendee']"}), |
| 251 | + 'from_launchpad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 252 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 253 | + 'meeting': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Meeting']"}), |
| 254 | + 'participation': ('django.db.models.fields.CharField', [], {'default': "'ATTENDING'", 'max_length': '32', 'null': 'True'}) |
| 255 | + }, |
| 256 | + 'schedule.room': { |
| 257 | + 'Meta': {'ordering': "('summit', 'name')", 'object_name': 'Room'}, |
| 258 | + 'end_utc': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_column': "'end'", 'blank': 'True'}), |
| 259 | + 'has_dial_in': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 260 | + 'icecast_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), |
| 261 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 262 | + 'irc_channel': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), |
| 263 | + 'name': ('summit.schedule.fields.NameField', [], {'max_length': '50'}), |
| 264 | + 'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
| 265 | + 'start_utc': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_column': "'start'", 'blank': 'True'}), |
| 266 | + 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}), |
| 267 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
| 268 | + 'tracks': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['schedule.Track']", 'symmetrical': 'False', 'blank': 'True'}), |
| 269 | + 'type': ('django.db.models.fields.CharField', [], {'default': "u'open'", 'max_length': '7'}) |
| 270 | + }, |
| 271 | + 'schedule.roombusy': { |
| 272 | + 'Meta': {'ordering': "('room', 'start_utc', 'end_utc')", 'object_name': 'RoomBusy'}, |
| 273 | + 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}), |
| 274 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 275 | + 'room': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'busy_set'", 'to': "orm['schedule.Room']"}), |
| 276 | + 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"}) |
| 277 | + }, |
| 278 | + 'schedule.slot': { |
| 279 | + 'Meta': {'ordering': "('summit', 'start_utc', 'end_utc')", 'object_name': 'Slot'}, |
| 280 | + 'end_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'end'"}), |
| 281 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 282 | + 'start_utc': ('django.db.models.fields.DateTimeField', [], {'db_column': "'start'"}), |
| 283 | + 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}), |
| 284 | + 'type': ('django.db.models.fields.CharField', [], {'default': "u'open'", 'max_length': '7'}) |
| 285 | + }, |
| 286 | + 'schedule.summit': { |
| 287 | + 'Meta': {'ordering': "('name',)", 'object_name': 'Summit'}, |
| 288 | + 'date_end': ('django.db.models.fields.DateField', [], {'null': 'True'}), |
| 289 | + 'date_start': ('django.db.models.fields.DateField', [], {'null': 'True'}), |
| 290 | + 'description': ('django.db.models.fields.TextField', [], {'max_length': '2047', 'blank': 'True'}), |
| 291 | + 'etherpad': ('django.db.models.fields.URLField', [], {'default': "'http://pad.ubuntu.com/'", 'max_length': '75'}), |
| 292 | + 'hashtag': ('django.db.models.fields.CharField', [], {'max_length': '25', 'blank': 'True'}), |
| 293 | + 'help_text': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), |
| 294 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 295 | + 'last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
| 296 | + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), |
| 297 | + 'managers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'managers'", 'blank': 'True', 'to': "orm['auth.User']"}), |
| 298 | + 'name': ('summit.schedule.fields.NameField', [], {'max_length': '50'}), |
| 299 | + 'qr': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}), |
| 300 | + 'schedulers': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'schedulers'", 'blank': 'True', 'to': "orm['auth.User']"}), |
| 301 | + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'}), |
| 302 | + 'state': ('django.db.models.fields.CharField', [], {'default': "u'sponsor'", 'max_length': '10'}), |
| 303 | + 'timezone': ('django.db.models.fields.CharField', [], {'max_length': '50'}), |
| 304 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
| 305 | + }, |
| 306 | + 'schedule.summitsprint': { |
| 307 | + 'Meta': {'ordering': "('summit', 'import_url')", 'object_name': 'SummitSprint'}, |
| 308 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 309 | + 'import_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), |
| 310 | + 'summit': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sprint_set'", 'to': "orm['schedule.Summit']"}) |
| 311 | + }, |
| 312 | + 'schedule.track': { |
| 313 | + 'Meta': {'ordering': "('summit', 'title', 'slug')", 'object_name': 'Track'}, |
| 314 | + 'allow_adjacent_sessions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
| 315 | + 'color': ('django.db.models.fields.CharField', [], {'default': "'FFFFFF'", 'max_length': '6'}), |
| 316 | + 'description': ('django.db.models.fields.TextField', [], {'max_length': '1000', 'null': 'True'}), |
| 317 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 318 | + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), |
| 319 | + 'summit': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['schedule.Summit']"}), |
| 320 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
| 321 | + }, |
| 322 | + 'sites.site': { |
| 323 | + 'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"}, |
| 324 | + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
| 325 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
| 326 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
| 327 | + } |
| 328 | + } |
| 329 | + |
| 330 | + complete_apps = ['schedule'] |
| 331 | |
| 332 | === modified file 'summit/schedule/models/meetingmodel.py' |
| 333 | --- summit/schedule/models/meetingmodel.py 2012-07-14 20:19:27 +0000 |
| 334 | +++ summit/schedule/models/meetingmodel.py 2012-10-04 13:59:28 +0000 |
| 335 | @@ -74,6 +74,7 @@ |
| 336 | (u'PENDING', u'Pending'), |
| 337 | (u'APPROVED', u'Approved'), |
| 338 | (u'DECLINED', u'Declined'), |
| 339 | + (u'REMOVED', u'Removed'), |
| 340 | ) |
| 341 | |
| 342 | summit = models.ForeignKey(Summit) |
| 343 | @@ -118,6 +119,7 @@ |
| 344 | related_name='scribe_set') |
| 345 | participants = models.ManyToManyField(Attendee, blank=True, |
| 346 | through='Participant') |
| 347 | + launchpad_blueprint_id = models.IntegerField(blank=True, null=True) |
| 348 | |
| 349 | class Meta: |
| 350 | app_label = 'schedule' |
| 351 | @@ -296,6 +298,12 @@ |
| 352 | |
| 353 | self.approved = 'APPROVED' |
| 354 | |
| 355 | + name = elem.get("name") |
| 356 | + if name: |
| 357 | + self.name = name[:100] |
| 358 | + else: |
| 359 | + self.name = None |
| 360 | + |
| 361 | status = elem.get("status") |
| 362 | if status: |
| 363 | self.status = status |
| 364 | |
| 365 | === modified file 'summit/schedule/models/summitmodel.py' |
| 366 | --- summit/schedule/models/summitmodel.py 2012-08-09 15:53:51 +0000 |
| 367 | +++ summit/schedule/models/summitmodel.py 2012-10-04 13:59:28 +0000 |
| 368 | @@ -233,14 +233,16 @@ |
| 369 | % self.name)] |
| 370 | return urls |
| 371 | |
| 372 | - def update_from_launchpad_response(self, response): |
| 373 | + def update_from_launchpad_response(self, response, options={}): |
| 374 | meetings = set() |
| 375 | - for elem in response.find("attendees").findall("person"): |
| 376 | - self.update_attendee_from_launchpad(elem) |
| 377 | - for elem in response.find("unscheduled").findall("meeting"): |
| 378 | - meeting = self.update_meeting_from_launchpad(elem) |
| 379 | - if meeting is not None: |
| 380 | - meetings.add(meeting) |
| 381 | + if not options.get('skip_attendees', False): |
| 382 | + for elem in response.find("attendees").findall("person"): |
| 383 | + self.update_attendee_from_launchpad(elem, options) |
| 384 | + if not options.get('skip_meetings', False): |
| 385 | + for elem in response.find("unscheduled").findall("meeting"): |
| 386 | + meeting = self.update_meeting_from_launchpad(elem, options) |
| 387 | + if meeting is not None: |
| 388 | + meetings.add(meeting) |
| 389 | return meetings |
| 390 | |
| 391 | def _get_sprint_info_from_launchpad(self, url): |
| 392 | @@ -264,26 +266,35 @@ |
| 393 | sprint_info = ElementTree.parse(export) |
| 394 | return sprint_info |
| 395 | |
| 396 | - def update_from_launchpad(self): |
| 397 | + def update_from_launchpad(self, options={}): |
| 398 | """Update from Launchpad data.""" |
| 399 | in_lp = set() |
| 400 | urls = self.launchpad_sprint_import_urls() |
| 401 | for url in urls: |
| 402 | print "Importing from %s" % url |
| 403 | sprint_info = self._get_sprint_info_from_launchpad(url) |
| 404 | - in_lp |= self.update_from_launchpad_response(sprint_info) |
| 405 | + in_lp |= self.update_from_launchpad_response(sprint_info, options) |
| 406 | + |
| 407 | + if not options.get('skip_meetings', False): |
| 408 | + in_db = set(m for m in self.meeting_set.exclude(launchpad_blueprint_id__isnull=True)) |
| 409 | + |
| 410 | + for extra in in_db.difference(in_lp): |
| 411 | + if not len(extra.agenda_set.all()): |
| 412 | + print "Marking %s as removed" % extra.name |
| 413 | + extra.approved='REMOVED' |
| 414 | + extra.save() |
| 415 | |
| 416 | self.last_update = datetime.utcnow() |
| 417 | self.save() |
| 418 | |
| 419 | - def update_attendee_from_launchpad(self, elem): |
| 420 | + def update_attendee_from_launchpad(self, elem, options={}): |
| 421 | """Update or create single attendee from Launchpad data.""" |
| 422 | + |
| 423 | username = elem.get("name", "") |
| 424 | if not username: |
| 425 | return |
| 426 | |
| 427 | print "user %s" % username |
| 428 | - |
| 429 | try: |
| 430 | attendee = self.attendee_set.get(user__username__exact=username[:30]) |
| 431 | except ObjectDoesNotExist: |
| 432 | @@ -298,10 +309,15 @@ |
| 433 | start=datetime.utcnow(), |
| 434 | end=datetime.utcnow()) |
| 435 | |
| 436 | + |
| 437 | attendee.update_from_launchpad(elem) |
| 438 | |
| 439 | - def update_meeting_from_launchpad(self, elem): |
| 440 | + def update_meeting_from_launchpad(self, elem, options={}): |
| 441 | """Update or create single meeting from Launchpad data.""" |
| 442 | + bp_id = elem.get("id", None) |
| 443 | + if not bp_id: |
| 444 | + return |
| 445 | + |
| 446 | full_name = elem.get("name", "") |
| 447 | if not full_name: |
| 448 | return |
| 449 | @@ -309,10 +325,13 @@ |
| 450 | |
| 451 | print "meeting %s" % name |
| 452 | meeting = "" |
| 453 | + |
| 454 | + slot_length = options.get('slots', 1) |
| 455 | + |
| 456 | try: |
| 457 | - meeting = self.meeting_set.get(name__exact=name) |
| 458 | + meeting = self.meeting_set.get(launchpad_blueprint_id=bp_id) |
| 459 | except ObjectDoesNotExist: |
| 460 | - meeting = self.meeting_set.create(name=name, title=full_name[:100]) |
| 461 | + meeting = self.meeting_set.create(name=name, title=full_name[:100], slots=slot_length, launchpad_blueprint_id=bp_id) |
| 462 | except: |
| 463 | pass |
| 464 | |
| 465 | |
| 466 | === modified file 'summit/schedule/tests.py' |
| 467 | --- summit/schedule/tests.py 2012-09-09 21:15:17 +0000 |
| 468 | +++ summit/schedule/tests.py 2012-10-04 13:59:28 +0000 |
| 469 | @@ -1667,7 +1667,7 @@ |
| 470 | |
| 471 | def test_update_meeting_trims_name(self): |
| 472 | summit = factory.make_one(Summit) |
| 473 | - elem = LaunchpadExportNode(name="very " * 20 + "longname") |
| 474 | + elem = LaunchpadExportNode(name="very " * 20 + "longname", id="42") |
| 475 | meeting = summit.update_meeting_from_launchpad(elem) |
| 476 | # Names are truncated at 100 chars. |
| 477 | expected_name = "very " * 20 |
| 478 | @@ -1678,7 +1678,7 @@ |
| 479 | summit = factory.make_one(Summit) |
| 480 | name = "meeting-name" |
| 481 | title = "other-title" |
| 482 | - elem = LaunchpadExportNode(name=name) |
| 483 | + elem = LaunchpadExportNode(name=name, id="42") |
| 484 | summit.meeting_set.create(name=name, title=title) |
| 485 | meeting = summit.update_meeting_from_launchpad(elem) |
| 486 | self.assertEqual(name, meeting.name) |
| 487 | @@ -1695,7 +1695,7 @@ |
| 488 | def test_update_from_launchpad_response_handles_no_name(self): |
| 489 | summit = factory.make_one(Summit) |
| 490 | elem = self.get_basic_launchpad_response() |
| 491 | - meeting_node = LaunchpadExportNode(name=None) |
| 492 | + meeting_node = LaunchpadExportNode(name=None, id="42") |
| 493 | elem.find("unscheduled").add_child("meeting", meeting_node) |
| 494 | meetings = summit.update_from_launchpad_response(elem) |
| 495 | self.assertEqual(set(), meetings) |
| 496 | @@ -1738,16 +1738,30 @@ |
| 497 | summit = factory.make_one(Summit) |
| 498 | def get_sprint_info(url): |
| 499 | elem = self.get_basic_launchpad_response() |
| 500 | - meeting_node = LaunchpadExportNode(name="foo") |
| 501 | + meeting_node = LaunchpadExportNode(name="foo", id="42") |
| 502 | elem.find("unscheduled").add_child("meeting", meeting_node) |
| 503 | return elem |
| 504 | summit._get_sprint_info_from_launchpad = get_sprint_info |
| 505 | summit.update_from_launchpad() |
| 506 | self.assertEqual(1, summit.meeting_set.all().count()) |
| 507 | |
| 508 | + def test_update_from_launchpad_does_renames(self): |
| 509 | + summit = factory.make_one(Summit) |
| 510 | + meeting = summit.meeting_set.create(name="name", launchpad_blueprint_id="42") |
| 511 | + def get_sprint_info(url): |
| 512 | + elem = self.get_basic_launchpad_response() |
| 513 | + meeting_node = LaunchpadExportNode(name="other", id="42") |
| 514 | + elem.find("unscheduled").add_child("meeting", meeting_node) |
| 515 | + return elem |
| 516 | + summit._get_sprint_info_from_launchpad = get_sprint_info |
| 517 | + summit.update_from_launchpad() |
| 518 | + # Since both had blueprint id 42, it should just update the existing meeting |
| 519 | + self.assertEqual(0, summit.meeting_set.filter(name__exact="name").count()) |
| 520 | + self.assertEqual(1, summit.meeting_set.filter(name__exact="other").count()) |
| 521 | + |
| 522 | def test_update_from_launchpad_deletes_unseen_meetings(self): |
| 523 | summit = factory.make_one(Summit) |
| 524 | - meeting = summit.meeting_set.create(spec_url='test_url', name="name") |
| 525 | + meeting = summit.meeting_set.create(spec_url='test_url', name="name", launchpad_blueprint_id="42") |
| 526 | def get_sprint_info(url): |
| 527 | elem = self.get_basic_launchpad_response() |
| 528 | meeting_node = LaunchpadExportNode(name="other") |
| 529 | @@ -1755,14 +1769,27 @@ |
| 530 | return elem |
| 531 | summit._get_sprint_info_from_launchpad = get_sprint_info |
| 532 | summit.update_from_launchpad() |
| 533 | - self.assertEqual(0, summit.meeting_set.filter(name__exact="name").count()) |
| 534 | + self.assertEqual(1, summit.meeting_set.filter(name__exact="name").count()) |
| 535 | + self.assertEqual(0, summit.meeting_set.filter(name__exact="name").exclude(approved='REMOVED').count()) |
| 536 | + |
| 537 | + def test_update_from_launchpad_doesnt_delete_meetings_with_spec_url(self): |
| 538 | + summit = factory.make_one(Summit) |
| 539 | + meeting = summit.meeting_set.create(spec_url='http://example.com/foo', name="name", launchpad_blueprint_id="42") |
| 540 | + def get_sprint_info(url): |
| 541 | + elem = self.get_basic_launchpad_response() |
| 542 | + meeting_node = LaunchpadExportNode(name="other", id="43") |
| 543 | + elem.find("unscheduled").add_child("meeting", meeting_node) |
| 544 | + return elem |
| 545 | + summit._get_sprint_info_from_launchpad = get_sprint_info |
| 546 | + summit.update_from_launchpad() |
| 547 | + self.assertEqual(1, summit.meeting_set.filter(name__exact="name").count()) |
| 548 | |
| 549 | def test_update_from_launchpad_doesnt_delete_meetings_with_no_spec_url(self): |
| 550 | summit = factory.make_one(Summit) |
| 551 | - meeting = summit.meeting_set.create(spec_url='', name="name") |
| 552 | + meeting = summit.meeting_set.create(spec_url='', name="name", launchpad_blueprint_id="42") |
| 553 | def get_sprint_info(url): |
| 554 | elem = self.get_basic_launchpad_response() |
| 555 | - meeting_node = LaunchpadExportNode(name="other") |
| 556 | + meeting_node = LaunchpadExportNode(name="other", id="43") |
| 557 | elem.find("unscheduled").add_child("meeting", meeting_node) |
| 558 | return elem |
| 559 | summit._get_sprint_info_from_launchpad = get_sprint_info |

When running -M I saw:
user yingscreative r-prior- release- feedback r-fwts- features r-ubuntu- drivers- common- and-lbm r-uefi- update- from-intel
user zsombi
user nncrash
will delete foundations-
will delete hardware-
will delete hardware-
will delete hardware-
I'm not sure that imported meetings should be deleted just because we ran a -M... Thoughts?