diff --git a/ietf/bin/create-break-sessions b/ietf/bin/create-break-sessions index db12bf684..842c9c251 100755 --- a/ietf/bin/create-break-sessions +++ b/ietf/bin/create-break-sessions @@ -22,8 +22,8 @@ scheduled = SessionStatusName.objects.get(slug='sched') for meeting in Meeting.objects.filter(type="ietf").order_by("date"): print "Checking %s schedules ..." % meeting - brk, __ = Session.objects.get_or_create(meeting=meeting, group=secretariat, requested_by=system, status=scheduled, name='Break', ) - reg, __ = Session.objects.get_or_create(meeting=meeting, group=secretariat, requested_by=system, status=scheduled, name='Registration', ) + brk, __ = Session.objects.get_or_create(meeting=meeting, group=secretariat, requested_by=system, status=scheduled, name='Break', type_id='break',) + reg, __ = Session.objects.get_or_create(meeting=meeting, group=secretariat, requested_by=system, status=scheduled, name='Registration', type_id='reg',) for schedule in meeting.schedule_set.all(): print " Checking for missing Break and Reg sessions in %s" % schedule diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 7dd8b3b4d..e865b2066 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -527,6 +527,7 @@ class DocTestCase(TestCase): status = SessionStatusName.objects.create(slug='scheduled', name='Scheduled'), modified = datetime.datetime.now(), requested_by = Person.objects.get(user__username="marschairman"), + type_id = "session", ) SessionPresentation.objects.create(session=session, document=doc, rev=doc.rev) diff --git a/ietf/doc/tests_material.py b/ietf/doc/tests_material.py index ea88ba8c2..47569fa0a 100644 --- a/ietf/doc/tests_material.py +++ b/ietf/doc/tests_material.py @@ -148,6 +148,7 @@ class GroupMaterialTests(TestCase): status = SessionStatusName.objects.create(slug='scheduled', name='Scheduled'), modified = datetime.datetime.now(), requested_by = Person.objects.get(user__username="marschairman"), + type_id="session", ) SessionPresentation.objects.create(session=session, document=doc, rev=doc.rev) diff --git a/ietf/group/migrations/0005_auto_20150504_0726.py b/ietf/group/migrations/0005_auto_20150504_0726.py new file mode 100644 index 000000000..5690ad9f4 --- /dev/null +++ b/ietf/group/migrations/0005_auto_20150504_0726.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations + +def create_iab_roles(apps, schema_editor): + Role = apps.get_model('group','Role') + Group = apps.get_model('group','Group') + Person = apps.get_model('person','Person') + + iab = Group.objects.get(acronym='iab') + + iab_names = [ + 'Jari Arkko', + 'Mary Barnes', + 'Marc Blanchet', + 'Ralph Droms', + 'Ted Hardie', + 'Joe Hildebrand', + 'Russ Housley', + 'Erik Nordmark', + 'Robert Sparks', + 'Andrew Sullivan', + 'Dave Thaler', + 'Brian Trammell', + 'Suzanne Woolf', + ] + + for name in iab_names: + person = Person.objects.get(name=name) + person.role_set.add(Role(name_id='member',group=iab,person=person,email_id=person.email_set.filter(active=True).order_by('-time').first().address)) + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0004_auto_20150430_0847'), + ] + + operations = [ + migrations.RunPython(create_iab_roles), + ] diff --git a/ietf/ietfauth/utils.py b/ietf/ietfauth/utils.py index f871bc006..fc9647093 100644 --- a/ietf/ietfauth/utils.py +++ b/ietf/ietfauth/utils.py @@ -46,6 +46,7 @@ def has_role(user, role_names, *args, **kwargs): role_qs = { "Area Director": Q(person=person, name__in=("pre-ad", "ad"), group__type="area", group__state="active"), "Secretariat": Q(person=person, name="secr", group__acronym="secretariat"), + "IAB" : Q(person=person, name="member", group__acronym="iab"), "IANA": Q(person=person, name="auth", group__acronym="iana"), "RFC Editor": Q(person=person, name="auth", group__acronym="rfceditor"), "ISE" : Q(person=person, name="chair", group__acronym="ise"), diff --git a/ietf/meeting/ajax.py b/ietf/meeting/ajax.py index 0465f3d23..27cd4d79e 100644 --- a/ietf/meeting/ajax.py +++ b/ietf/meeting/ajax.py @@ -158,6 +158,8 @@ AddSlotForm = modelform_factory(TimeSlot, exclude=('meeting','name','location',' # no authorization required to list. def timeslot_slotlist(request, mtg): slots = mtg.timeslot_set.all() + # Restrict graphical editing to slots of type 'session' for now + slots = slots.filter(type__slug='session') json_array=[] for slot in slots: json_array.append(slot.json_dict(request.build_absolute_uri('/'))) diff --git a/ietf/meeting/helpers.py b/ietf/meeting/helpers.py index 0f6581176..bac9695b3 100644 --- a/ietf/meeting/helpers.py +++ b/ietf/meeting/helpers.py @@ -66,7 +66,7 @@ def build_all_agenda_slices(meeting): time_slices = [] date_slices = {} - for ts in meeting.timeslot_set.exclude(type__in=['reg','break']).order_by('time','name'): + for ts in meeting.timeslot_set.filter(type__in=['session',]).order_by('time','name'): ymd = ts.time.date() if ymd not in date_slices and ts.location != None: @@ -80,13 +80,10 @@ def build_all_agenda_slices(meeting): time_slices.sort() return time_slices,date_slices -def get_scheduledsessions_from_schedule(schedule): - ss = schedule.scheduledsession_set.filter(timeslot__location__isnull = False).exclude(session__isnull = True).order_by('timeslot__time','timeslot__name','session__group__group') - - return ss - def get_all_scheduledsessions_from_schedule(schedule): - ss = schedule.scheduledsession_set.filter(timeslot__location__isnull = False).order_by('timeslot__time','timeslot__name') + ss = schedule.scheduledsession_set.filter(timeslot__location__isnull = False) + ss = ss.filter(session__type__slug='session') + ss = ss.order_by('timeslot__time','timeslot__name') return ss diff --git a/ietf/meeting/migrations/0006_auto_20150318_1116.py b/ietf/meeting/migrations/0006_auto_20150318_1116.py new file mode 100644 index 000000000..1049d26d9 --- /dev/null +++ b/ietf/meeting/migrations/0006_auto_20150318_1116.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import datetime +from django.db import migrations + + +def backfill_91_other_meetings(apps, schema_editor): + + Meeting = apps.get_model('meeting', 'Meeting') + Schedule = apps.get_model('meeting', 'Schedule') + ScheduledSession = apps.get_model('meeting', 'ScheduledSession') + Room = apps.get_model('meeting', 'Room') + Group = apps.get_model('group', 'Group') + Person = apps.get_model('person', 'Person') + + ietf91 = Meeting.objects.filter(number=91).first() + + if not ietf91: + print "IETF91 not found, no data changed" + else: + agenda91 = Schedule.objects.get(meeting=ietf91,pk=ietf91.agenda.pk) + + south_pacific_1 = Room.objects.get(meeting=ietf91,name="South Pacific 1") + south_pacific_2 = Room.objects.get(meeting=ietf91,name="South Pacific 2") + rainbow_12 = Room.objects.get(meeting=ietf91,name="Rainbow Suite 1/2") + lehua_suite = Room.objects.get(meeting=ietf91,name="Lehua Suite") + kahili = Room.objects.get(meeting=ietf91,name="Kahili") + coral_2 = Room.objects.get(meeting=ietf91,name="Coral 2") + + south_pacific_3 = Room.objects.create(meeting=ietf91,name="South Pacific 3",capacity=20) + rainbow_suite_3 = Room.objects.create(meeting=ietf91,name="Rainbow Suite 3",capacity=20) + rainbow_23 = Room.objects.create(meeting=ietf91,name="Rainbow Suite 2/3",capacity=210) + south_pacific_34 = Room.objects.create(meeting=ietf91,name="South Pacific 3/4",capacity=210) + iolani_67 = Room.objects.create(meeting=ietf91,name="Iolani 6/7",capacity=40) + sea_pearl_12 = Room.objects.create(meeting=ietf91,name="Sea Pearl 1/2",capacity=40) + sea_pearl_2 = Room.objects.create(meeting=ietf91,name="Sea Pearl 2",capacity=20) + coral_lounge = Room.objects.create(meeting=ietf91,name="Coral Lounge", capacity=1200) + hibiscus = Room.objects.create(meeting=ietf91,name="Hibiscus", capacity=20) + tiare = Room.objects.create(meeting=ietf91,name="Tiare Suite", capacity=20) + + iesg = Group.objects.get(acronym='iesg') + iab = Group.objects.get(acronym='iab') + rsoc = Group.objects.get(acronym='rsoc') + iaoc = Group.objects.get(acronym='iaoc') + nomcom = Group.objects.get(acronym='nomcom2014') + isoc = Group.objects.get(acronym='isoc') + secr = Group.objects.get(acronym='secretariat') + isocbot = Group.objects.create(acronym='isocbot',name="Internet Society Board of Trustees",state_id='active',type_id='isoc',parent=isoc) + isocfell = Group.objects.create(acronym='isocfell',name="Internet Society Fellows",state_id='active',type_id='isoc',parent=isoc) + + system = Person.objects.get(name='(System)') + + for d, h, m, duration, type_id, groups, room, slotname, label in [ + ( 9, 8, 0, 120, 'offagenda', [secr], rainbow_suite_3, 'WEIRDS Interop', 'WEIRDS Interop'), + ( 9, 8, 30, 90, 'lead', [iesg], south_pacific_2, 'Breakfast', None), + ( 9, 9, 0, 240, 'offagenda', [secr], lehua_suite, 'RMCAT Interim', 'RMCAT Interim Meeting'), + ( 9, 9, 0, 60, 'lead', [nomcom], iolani_67, 'Breakfast', 'Nomcom Breakfast'), + ( 9, 9, 0, 150, 'lead', [iesg], south_pacific_2, 'Meeting', None), + ( 9, 9, 0, 360, 'offagenda', [secr], hibiscus, 'Meeting', 'RootOPS'), + ( 9, 9, 30, 360, 'offagenda', [secr], kahili, 'TLS Interim', 'TLS WG Interim'), + ( 9, 11, 0, 480, 'offagenda', [secr], coral_lounge, 'T-Shirt Distribution', 'T-shirt Distribution'), + ( 9, 11, 30, 150, 'lead', [iesg], south_pacific_2, 'Lunch', 'IESG Lunch with the IAB'), + ( 9, 11, 30, 150, 'lead', [iab], south_pacific_2, 'Lunch', 'IAB Lunch with the IESG'), + ( 9, 12, 0, 360, 'offagenda', [secr], south_pacific_1, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 9, 14, 0, 180, 'lead', [iab], south_pacific_2, 'Meeting', None), + ( 9, 16, 0, 120, 'offagenda', [secr], coral_2, 'Meeting', 'Web Object Encryption'), + ( 9, 17, 0, 120, 'offagenda', [secr], sea_pearl_12, 'Reception', "Companion's Reception"), # Should this appear on agenda? + ( 9, 19, 0, 180, 'offagenda', [isocfell], rainbow_23, 'Dinner', 'ISOC Fellows Reception/Dinner'), + ( 9, 19, 0, 180, 'offagenda', [secr], lehua_suite, 'Meeting', 'Huawei'), + ( 9, 21, 0, 180, 'lead', [secr], sea_pearl_12, 'Gathering', 'AMS/IESG/IAB/IAOC Gathering'), + ( 10, 0, 0, 1440, 'offagenda', [secr], south_pacific_1, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 10, 7, 0, 120, 'lead', [iesg], south_pacific_2, 'Breakfast', 'IESG Breakfast with the IAB'), + ( 10, 7, 0, 120, 'lead', [iab], south_pacific_2, 'Breakfast', 'IAB Breakfast with the IESG'), + ( 10, 7, 0, 120, 'lead', [nomcom], iolani_67, 'Breakfast', 'Nomcom Breakfast'), + ( 10, 8, 0, 600, 'offagenda', [secr], coral_lounge, 'T-shirt Distribution', 'T-shirt Distribution'), + ( 10, 11, 30, 90, 'offagenda', [secr], south_pacific_2, 'Meeting', 'OPS Directorate Meeting'), + ( 10, 11, 30, 90, 'offagenda', [secr], rainbow_suite_3, 'Meeting', 'IETF/3GPP Meeting'), + ( 10, 11, 30, 90, 'offagenda', [secr], lehua_suite, 'Meeting', 'RTG Area Meeting'), + ( 10, 19, 0, 240, 'offagenda', [secr], south_pacific_2, 'Meeting', 'Huawei'), + ( 11, 0, 0, 1440, 'offagenda', [secr], south_pacific_1, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 11, 7, 0, 120, 'lead', [iesg], south_pacific_2, 'Breakfast', None), + ( 11, 7, 0, 120, 'lead', [nomcom], iolani_67, 'Breakfast', 'Nomcom Breakfast'), + ( 11, 7, 0, 120, 'lead', [iab], rainbow_suite_3, 'Breakfast', None), + ( 11, 7, 0, 60, 'lead', [iab], tiare, 'Meeting', 'Vendor Selection Committee Meeting'), + ( 11, 8, 0, 600, 'offagenda', [secr], coral_lounge, 'T-shirt Distribution', 'T-shirt Distribution'), + ( 11, 9, 0, 90, 'offagenda', [secr], south_pacific_2, 'Meeting', 'DHCPv6bis Team Meeting'), + ( 11, 11, 30, 90, 'offagenda', [secr], south_pacific_2, 'Meeting', 'SECdir Meeting'), + ( 11, 11, 30, 90, 'offagenda', [secr], rainbow_suite_3, 'Lunch', 'RSAG/ISEB Lunch'), + ( 11, 16, 0, 240, 'offagenda', [secr], south_pacific_2, 'Meeting', 'Verisign Corporate Meeting'), + ( 12, 0, 0, 1440, 'offagenda', [secr], south_pacific_1, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 12, 7, 30, 90, 'lead', [iaoc], south_pacific_3, 'Breakfast', None), + ( 12, 7, 0, 120, 'lead', [nomcom], iolani_67, 'Breakfast', 'Nomcom Breakfast'), + ( 12, 8, 0, 540, 'offagenda', [secr], coral_lounge, 'T-shirt Distribution', 'T-shirt Distribution'), + ( 12, 8, 0, 240, 'offagenda', [secr], south_pacific_2, 'Meeting', 'DIME WG'), + ( 12, 11, 30, 90, 'offagenda', [secr], rainbow_suite_3, 'Lunch', 'RFC Editor Lunch'), + ( 12, 15, 0, 120, 'offagenda', [secr], south_pacific_2, 'Meeting', 'YANG Advice'), + ( 12, 17, 0, 240, 'offagenda', [secr], rainbow_suite_3, 'Meeting', 'Huawei (POC Wil Liu)'), + ( 12, 20, 0, 150, 'offagenda', [secr], south_pacific_2, 'Meeting', 'ICANN SSAC'), + ( 13, 0, 0, 1440, 'offagenda', [secr], south_pacific_1, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 13, 7, 0, 120, 'lead', [iab], rainbow_suite_3, 'Breakfast', None), + ( 13, 7, 0, 120, 'lead', [nomcom], iolani_67, 'Breakfast', 'Nomcom Breakfast'), + ( 13, 11, 30, 90, 'lead', [iab], sea_pearl_2, 'Meeting', 'IAB Liaison Oversight'), + ( 13, 11, 30, 90, 'lead', [rsoc], rainbow_suite_3, 'Lunch', None), + ( 14, 0, 0, 900, 'offagenda', [secr], south_pacific_1, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 14, 7, 0, 120, 'lead', [nomcom], iolani_67, 'Breakfast', 'Nomcom Breakfast'), + ( 14, 11, 0, 360, 'offagenda', [isoc], south_pacific_34,'Meeeting', 'ISOC AC Meeting'), + ( 14, 13, 30, 90, 'lead', [iesg], south_pacific_2, 'Lunch', 'IESG Lunch with the IAB'), + ( 14, 13, 30, 90, 'lead', [iab], south_pacific_2, 'Lunch', 'IAB Lunch with the IESG'), + ( 14, 18, 0, 60, 'offagenda', [isocbot], rainbow_23, 'Reception', 'ISOC Board Reception for IETF Leadership'), + ( 14, 19, 0, 180, 'offagenda', [isocbot], rainbow_23, 'Dinner', 'ISOC Board Dinner for IETF Leadership'), + ( 15, 8, 0, 60, 'offagenda', [isocbot], rainbow_12, 'Breakfast', 'ISOC Board of Trustees Breakfast'), + ( 15, 8, 0, 540, 'offagenda', [isocbot], south_pacific_34,'Meeting', 'ISOC Board of Trustees Meeting'), + ( 15, 12, 0, 60, 'offagenda', [isocbot], rainbow_12, 'Lunch', 'ISOC Board of Trustees Lunch'), + ( 16, 8, 0, 60, 'offagenda', [isocbot], rainbow_12, 'Breakfast', 'ISOC Board of Trustees Breakfast'), + ( 16, 8, 0, 540, 'offagenda', [isocbot], south_pacific_34,'Meeting', 'ISOC Board of Trustees Meeting'), + ( 16, 12, 0, 60, 'offagenda', [isocbot], rainbow_12, 'Lunch', 'ISOC Board of Trustees Lunch'), + ]: + ts = ietf91.timeslot_set.create(type_id=type_id, name=slotname, + time=datetime.datetime(2014,11,d,h,m,0), + duration=datetime.timedelta(minutes=duration), + location=room,show_location=(type_id not in ['lead','offagenda'])) + for group in groups: + session = ietf91.session_set.create(name= label or "%s %s"%(group.acronym.upper(),slotname), + group=group, attendees=25, + requested=datetime.datetime(2014,11,1,0,0,0), + requested_by=system, status_id='sched') + ScheduledSession.objects.create(schedule=agenda91, timeslot=ts, session=session) + + + + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0005_auto_20150430_0847'), + ('name', '0004_auto_20150318_1140'), + ('group', '0004_auto_20150430_0847'), + ('person', '0004_auto_20150308_0440'), + ] + + operations = [ + migrations.RunPython(backfill_91_other_meetings) + ] diff --git a/ietf/meeting/migrations/0007_auto_20150429_1224.py b/ietf/meeting/migrations/0007_auto_20150429_1224.py new file mode 100644 index 000000000..43717f149 --- /dev/null +++ b/ietf/meeting/migrations/0007_auto_20150429_1224.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + +def extract_session_type_values(apps, schema_editor): + + Session = apps.get_model('meeting', 'Session') + + for s in Session.objects.all(): + t = s.scheduledsession_set.filter(schedule=models.F('schedule__meeting__agenda')).first() + if t and t.timeslot.type.slug != 'session': + s.type = t.timeslot.type + s.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('name', '0004_auto_20150318_1140'), + ('meeting', '0006_auto_20150318_1116'), + ] + + operations = [ + migrations.AddField( + model_name='session', + name='type', + field=models.ForeignKey(default='session', to='name.TimeSlotTypeName'), + preserve_default=False, + ), + migrations.RunPython(extract_session_type_values), + ] diff --git a/ietf/meeting/migrations/0008_auto_20150429_1346.py b/ietf/meeting/migrations/0008_auto_20150429_1346.py new file mode 100644 index 000000000..24485e2fd --- /dev/null +++ b/ietf/meeting/migrations/0008_auto_20150429_1346.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + +def extract_room_session_type_values(apps, schema_editor): + + Room = apps.get_model('meeting', 'Room') + + for r in Room.objects.all(): + for ts in r.timeslot_set.all(): + if ts.scheduledsession_set.filter(schedule=models.F('schedule__meeting__agenda')): + r.session_types.add(ts.type) + +class Migration(migrations.Migration): + + dependencies = [ + ('name', '0004_auto_20150318_1140'), + ('meeting', '0007_auto_20150429_1224'), + ] + + operations = [ + migrations.AddField( + model_name='room', + name='session_types', + field=models.ManyToManyField(to='name.TimeSlotTypeName', blank=True), + preserve_default=True, + ), + migrations.RunPython(extract_room_session_type_values), + ] diff --git a/ietf/meeting/migrations/0009_room_functional_name.py b/ietf/meeting/migrations/0009_room_functional_name.py new file mode 100644 index 000000000..4170f3b59 --- /dev/null +++ b/ietf/meeting/migrations/0009_room_functional_name.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0008_auto_20150429_1346'), + ] + + operations = [ + migrations.AddField( + model_name='room', + name='functional_name', + field=models.CharField(default='', max_length=255, blank=True), + preserve_default=False, + ), + ] diff --git a/ietf/meeting/migrations/0010_auto_20150501_0732.py b/ietf/meeting/migrations/0010_auto_20150501_0732.py new file mode 100644 index 000000000..7c52f7634 --- /dev/null +++ b/ietf/meeting/migrations/0010_auto_20150501_0732.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations + + +def add_91_room_functional_names(apps, schema_editor): + + map = { + 'Hibiscus': 'Breakout 3', + 'South Pacific 2': 'Meeting Room #6', + 'South Pacific 1': 'Terminal Room', + 'Coral 1': 'Breakout 4', + 'Coral 2': 'Breakout 5', + 'Coral 5': 'Breakout 6', + 'Coral 4': 'Breakout 7', + 'Coral 3': 'Breakout 8', + 'Great Lawn': 'Welcome Reception', + 'Rainbow Suite': 'Not Used', + 'Lehua Suite': 'Breakout 1', + 'Kahili': 'Breakout 2', + 'Rainbow Suite 1/2': 'Meeting Room #2 (IESG Meeting Room)', + 'Village Green': 'Meet and Greet', + 'South Pacific 3': 'Meeting Room #4 (IAOC/IAD Office)', + 'Rainbow Suite 3': 'Meeting Room #7', + 'Rainbow Suite 2/3': 'ISOC Dinner', + 'South Pacific 3/4': 'ISOC AC Meeting', + 'Iolani 6/7': 'Meeting Room #5 (NomCom Office)', + 'Sea Pearl 1/2': 'Reception', + 'Sea Pearl 2': 'Meeting Room #1 (IAB Meeting Room)', + 'Coral Lounge': 'Registration Area and Breaks', + 'Tiare Suite': 'Meeting Room #8 (RFC Office)', + } + + Room = apps.get_model('meeting', 'Room') + + for name,functional_name in map.items(): + Room.objects.filter(meeting__number=91,name=name).update(functional_name=functional_name) + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0009_room_functional_name'), + ] + + operations = [ + migrations.RunPython(add_91_room_functional_names), + ] + diff --git a/ietf/meeting/migrations/0011_ietf92_meetings.py b/ietf/meeting/migrations/0011_ietf92_meetings.py new file mode 100644 index 000000000..20b20caa6 --- /dev/null +++ b/ietf/meeting/migrations/0011_ietf92_meetings.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import datetime +from django.db import migrations + + +def backfill_92_other_meetings(apps, schema_editor): + + Meeting = apps.get_model('meeting', 'Meeting') + Schedule = apps.get_model('meeting', 'Schedule') + ScheduledSession = apps.get_model('meeting', 'ScheduledSession') + Room = apps.get_model('meeting', 'Room') + Session = apps.get_model('meeting', 'Session') + Group = apps.get_model('group', 'Group') + Person = apps.get_model('person', 'Person') + + ietf92 = Meeting.objects.filter(number=92).first() + + if not ietf92: + print "IETF92 not found, no data changed" + else: + + # Clear out one orphaned ill-configured Session object + qs = Session.objects.filter(meeting__number=92,name__icontains='beverage break').exclude(type_id='break') + if qs.count()==1: + qs.delete() + + agenda92 = Schedule.objects.get(meeting=ietf92,pk=ietf92.agenda.pk) + + map_existing = { + 'Regency Ballroom': 'Lounge', + 'Garden Terrace Level': 'Meet and Greet', + 'Royal': 'Breakout 1', + 'Continental': 'Breakout 2', + 'Far East': 'Breakout 3', + 'Oak ': 'Breakout 4', + 'Parisian': 'Breakout 5', + 'Venetian': 'Breakout 6', + 'Gold': 'Breakout 7', + 'International': 'Breakout 8', + 'Brasserie': 'Terminal Room', + 'State': 'Office #3 (Secretariat Office)', + 'French': 'Meeting Room #2 (IESG Meeting Room)', + } + + for name,functional_name in map_existing.items(): + Room.objects.filter(meeting__number=92,name=name).update(functional_name=functional_name) + + regency = Room.objects.get(meeting=ietf92,name='Regency Ballroom') + garden = Room.objects.get(meeting=ietf92,name='Garden Terrace Level') + royal = Room.objects.get(meeting=ietf92,name='Royal') + continental = Room.objects.get(meeting=ietf92,name='Continental') + far_east = Room.objects.get(meeting=ietf92,name='Far East') + oak = Room.objects.get(meeting=ietf92,name='Oak ') + #parisian = Room.objects.get(meeting=ietf92,name='Parisian') + #venetian = Room.objects.get(meeting=ietf92,name='Venetian') + #gold = Room.objects.get(meeting=ietf92,name='Gold') + #international = Room.objects.get(meeting=ietf92,name='International') + brasserie = Room.objects.get(meeting=ietf92,name='Brasserie') + state = Room.objects.get(meeting=ietf92,name='State') + #french = Room.objects.get(meeting=ietf92,name='French') + + executive = Room.objects.create(meeting=ietf92,name='Executive',functional_name='Meeting Room #4 (IAOC/IAD)',capacity=20) + regency_foyer = Room.objects.create(meeting=ietf92,name='Regency Foyer',functional_name='Registration',capacity=1200) + florentine = Room.objects.create(meeting=ietf92,name='Florentine',functional_name='Meeting Room #1 (IAB)', capacity=40) + pavilion = Room.objects.create(meeting=ietf92,name='Pavilion',functional_name='Meeting Room #6', capacity=80) + terrace = Room.objects.create(meeting=ietf92,name='Terrace',functional_name='Meeting Room #7', capacity=80) + panorama = Room.objects.create(meeting=ietf92,name='Panorama',functional_name='Companion Reception', capacity=200) + + regency.session_types.add('offagenda') + pavilion.session_types.add('offagenda') + pavilion.session_types.add('lead') + garden.session_types.add('lead') + panorama.session_types.add('offagenda') + executive.session_types.add('lead') + executive.session_types.add('offagenda') + regency_foyer.session_types.add('offagenda') + oak.session_types.add('offagenda') + continental.session_types.add('offagenda') + state.session_types.add('offagenda') + florentine.session_types.add('offagenda') + terrace.session_types.add('lead') + terrace.session_types.add('offagenda') + far_east.session_types.add('offagenda') + brasserie.session_types.add('offagenda') + royal.session_types.add('offagenda') + + iesg = Group.objects.get(acronym='iesg') + iab = Group.objects.get(acronym='iab') + iaoc = Group.objects.get(acronym='iaoc') + secr = Group.objects.get(acronym='secretariat') + + system = Person.objects.get(name='(System)') + + for d, h, m, duration, type_id, groups, room, slotname, label in [ + ( 20, 13, 0, 480, 'offagenda', [secr], brasserie, 'Setup', 'Hackathon: Setup'), + ( 20, 8, 0, 540, 'offagenda', [secr], executive, 'Meeting', 'DNS OARC Meeting'), + ( 21, 8, 0, 540, 'offagenda', [secr], executive, 'Meeting', 'DNS OARC Meeting'), + ( 22, 12, 0, 720, 'offagenda', [secr], brasserie, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 22, 11, 0, 480, 'offagenda', [secr], regency_foyer, 'T-Shirt Distribution', 'T-shirt Distribution'), + ( 22, 19, 0, 120, 'offagenda', [secr], state, 'Meeting', 'CJK Generation Panel coordination informal meeting'), + ( 22, 19, 0, 120, 'offagenda', [iab], florentine, 'Meeting', 'IAB PrivSec program'), + ( 22, 8, 30, 90, 'lead', [iesg], pavilion, 'Breakfast', None), + ( 22, 9, 0, 150, 'lead', [iesg], pavilion, 'Meeting', None), + ( 22, 11, 30, 150, 'lead', [iab], pavilion, 'Lunch', 'IAB Lunch with the IESG'), + ( 22, 11, 30, 150, 'lead', [iesg], pavilion, 'Lunch', 'IESG Lunch with the IAB'), + ( 22, 14, 0, 180, 'lead', [iab], pavilion, 'Meeting', None), + ( 22, 9, 0, 480, 'offagenda', [secr], terrace, 'Meeting', 'RootOPS'), + ( 22, 16, 30, 60, 'offagenda', [secr], panorama, 'Reception', "Companion's Reception"), # Should this appear on agenda? + ( 22, 21, 0, 180, 'lead', [secr], garden, 'Gathering', 'AMS/IESG/IAB/IAOC Gathering'), + ( 22, 9, 0, 480, 'offagenda', [secr], royal, 'ICNRG', 'ICNRG'), + ( 22, 19, 0, 180, 'offagenda', [secr], royal, 'Meeting', 'Huawei'), + ( 22, 12, 30, 240, 'offagenda', [secr], continental, 'Meeting', 'Verisign ROA Workshop'), + ( 22, 15, 15, 165, 'offagenda', [secr], far_east, 'Meeting', 'RSSAC'), + ( 22, 9, 0, 150, 'offagenda', [secr], oak, 'Meeting', 'Ericsson'), + ( 23, 0, 0, 1440, 'offagenda', [secr], brasserie, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 23, 8, 0, 600, 'offagenda', [secr], regency_foyer, 'T-Shirt Distribution', 'T-shirt Distribution'), + ( 23, 0, 0, 1440, 'offagenda', [secr], regency, 'Lounge', 'Lounge'), + ( 23, 11, 30, 180, 'offagenda', [secr], executive, 'Lunch', 'ICANN Lunch'), + ( 23, 7, 0, 120, 'lead', [iesg], pavilion, 'Breakfast', 'IESG Breakfast with the IAB'), + ( 23, 7, 0, 120, 'lead', [iab], pavilion, 'Breakfast', 'IAB Breakfast with the IESG'), + ( 23, 11, 30, 90, 'offagenda', [secr], pavilion, 'Meeting', 'OPS Directorate Meeting'), + ( 23, 19, 0, 120, 'offagenda', [secr], pavilion, 'Meeting', 'ACE'), + ( 23, 7, 30, 90, 'offagenda', [secr], terrace, 'Meeting', 'NRO ECG'), + ( 23, 11, 30, 90, 'offagenda', [secr], terrace, 'Meeting', 'IETF/3GPP Meeting'), + ( 23, 19, 0, 120, 'offagenda', [secr], terrace, 'Meeting', 'I2NSF'), + ( 23, 18, 50, 60, 'offagenda', [secr], royal, 'Meeting', 'Captive Portal Bar BOF'), + ( 24, 0, 0, 1440, 'offagenda', [secr], brasserie, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 24, 8, 0, 600, 'offagenda', [secr], regency_foyer, 'T-Shirt Distribution', 'T-shirt Distribution'), + ( 24, 0, 0, 1440, 'offagenda', [secr], regency, 'Lounge', 'Lounge'), + ( 24, 11, 30, 90, 'offagenda', [secr], state, 'Meeting', 'HIAPS'), + ( 24, 16, 30, 120, 'offagenda', [secr], state, 'Meeting', 'PDF Draft Review'), + ( 24, 7, 0, 120, 'lead', [iesg], pavilion, 'Breakfast', None), + ( 24, 11, 30, 90, 'offagenda', [secr], pavilion, 'Meeting', 'SECdir Meeting'), + ( 24, 7, 0, 120, 'lead', [iab], terrace, 'Breakfast', None), + ( 24, 9, 0, 120, 'offagenda', [secr], terrace, 'Meeting', 'ICNN DRZK Design Team'), + ( 24, 11, 30, 90, 'offagenda', [secr], terrace, 'Lunch', 'RSAG/ISEB Lunch'), + ( 24, 13, 0, 120, 'offagenda', [secr], terrace, 'Meeting', 'SACM'), + ( 24, 15, 0, 90, 'offagenda', [secr], terrace, 'Meeting', 'RSOC Meeting'), + ( 24, 17, 30, 60, 'offagenda', [secr], terrace, 'Meeting', 'SACM'), + ( 24, 11, 30, 90, 'offagenda', [secr], royal, 'Meeting', 'IoT Directorate'), + ( 25, 0, 0, 1440, 'offagenda', [secr], brasserie, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 25, 8, 0, 600, 'offagenda', [secr], regency_foyer, 'T-Shirt Distribution', 'T-shirt Distribution'), + ( 25, 0, 0, 1440, 'offagenda', [secr], regency, 'Lounge', 'Lounge'), + ( 25, 8, 0, 60, 'offagenda', [secr], state, 'Meeting', 'SFC Control Plane Offline Discussion'), + ( 25, 19, 0, 240, 'offagenda', [secr], state, 'Meeting', 'WWG'), + ( 25, 8, 0, 60, 'offagenda', [secr], florentine, 'Meeting', 'IAB Name Resolution'), + ( 25, 6, 45, 135, 'lead', [iaoc], executive, 'Breakfast', None), + ( 25, 11, 30, 90, 'offagenda', [secr], pavilion, 'Meeting', 'RMCAT'), + ( 25, 19, 0, 120, 'offagenda', [secr], pavilion, 'Meeting', 'I2NSF'), + ( 25, 8, 0, 60, 'offagenda', [secr], terrace, 'Meeting', 'IETF/IEEE 802 Coordination'), + ( 25, 11, 30, 90, 'offagenda', [secr], terrace, 'Lunch', 'RFC Editor Lunch'), + ( 25, 19, 30, 120, 'offagenda', [secr], terrace, 'Dinner', 'SSAC Dinner'), + ( 26, 0, 0, 1440, 'offagenda', [secr], brasserie, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 26, 8, 0, 600, 'offagenda', [secr], regency_foyer, 'T-Shirt Distribution', 'T-shirt Distribution'), + ( 26, 0, 0, 1440, 'offagenda', [secr], regency, 'Lounge', 'Lounge'), + ( 26, 7, 30, 90, 'offagenda', [secr], state, 'Breakfast', 'EDU Team Breakfast'), + ( 26, 14, 0, 120, 'offagenda', [secr], state, 'Meeting', 'JJB'), + ( 26, 11, 30, 90, 'offagenda', [secr], florentine, 'Meeting', 'IAB Liaison Oversight'), + ( 26, 18, 0, 150, 'offagenda', [secr], pavilion, 'Meeting', '6LO Security Discussion'), + ( 26, 7, 0, 120, 'lead', [iab], terrace, 'Breakfast', None), + ( 26, 17, 40, 60, 'offagenda', [secr], terrace, 'Meeting', 'SACM'), + ( 26, 19, 30, 150, 'offagenda', [secr], royal, 'Meeting', 'Lavabit'), + ( 27, 0, 0, 900, 'offagenda', [secr], brasserie, 'Terminal Room', 'Terminal Room Open to Attendees'), + ( 27, 7, 30, 90, 'offagenda', [secr], executive, 'Meeting', 'Post-Con with Ray'), + ( 27, 7, 30, 75, 'offagenda', [secr], state, 'Breakfast', 'Gen-art'), + ( 27, 13, 30, 90, 'lead', [iab], pavilion, 'Lunch', 'IAB Lunch with the IESG'), + ( 27, 13, 30, 90, 'lead', [iesg], pavilion, 'Lunch', 'IESG Lunch with the IAB'), + ]: + + ts = ietf92.timeslot_set.create(type_id=type_id, name=slotname, + time=datetime.datetime(2015,3,d,h,m,0), + duration=datetime.timedelta(minutes=duration), + location=room,show_location=(type_id not in ['lead','offagenda'])) + for group in groups: + session = ietf92.session_set.create(name= label or "%s %s"%(group.acronym.upper(),slotname), + group=group, attendees=25, + requested=datetime.datetime(2014,11,1,0,0,0), + requested_by=system, status_id='sched',type_id=type_id) + ScheduledSession.objects.create(schedule=agenda92, timeslot=ts, session=session) + + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0010_auto_20150501_0732'), + ('name', '0004_auto_20150318_1140'), + ('group', '0004_auto_20150430_0847'), + ('person', '0004_auto_20150308_0440'), + ] + + operations = [ + migrations.RunPython(backfill_92_other_meetings) + ] diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index 23e7cc855..bcecac490 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -161,7 +161,10 @@ class Meeting(models.Model): @property def sessions_that_can_meet(self): - return self.session_set.exclude(status__slug='notmeet').exclude(status__slug='disappr').exclude(status__slug='deleted').exclude(status__slug='apprw') + qs = self.session_set.exclude(status__slug='notmeet').exclude(status__slug='disappr').exclude(status__slug='deleted').exclude(status__slug='apprw') + # Restrict graphical scheduling to meeting requests (Sessions) of type 'session' for now + qs = qs.filter(type__slug='session') + return qs def sessions_that_can_be_placed(self): from django.db.models import Q @@ -284,8 +287,10 @@ class ResourceAssociation(models.Model): class Room(models.Model): meeting = models.ForeignKey(Meeting) name = models.CharField(max_length=255) + functional_name = models.CharField(max_length=255, blank = True) capacity = models.IntegerField(null=True, blank=True) resources = models.ManyToManyField(ResourceAssociation, blank = True) + session_types = models.ManyToManyField(TimeSlotTypeName, blank = True) def __unicode__(self): return "%s size: %s" % (self.name, self.capacity) @@ -370,7 +375,8 @@ class TimeSlot(models.Model): return u"%s: %s-%s %s, %s" % (self.meeting.number, self.time.strftime("%m-%d %H:%M"), (self.time + self.duration).strftime("%H:%M"), self.name, location) def end_time(self): return self.time + self.duration - def get_location(self): + + def get_hidden_location(self): location = self.location if location: location = location.name @@ -378,10 +384,24 @@ class TimeSlot(models.Model): location = self.meeting.reg_area elif self.type_id == "break": location = self.meeting.break_area + return location + + def get_location(self): + location = self.get_hidden_location() if not self.show_location: location = "" return location + def get_functional_location(self): + name_parts = [] + room = self.location + if room and room.functional_name: + name_parts.append(room.functional_name) + location = self.get_hidden_location() + if location: + name_parts.append(location) + return ' - '.join(name_parts) + @property def tz(self): if self.meeting.time_zone: @@ -626,8 +646,7 @@ class Schedule(models.Model): .distinct() ) def groups(self): - return Group.objects.filter(type__slug__in=['wg', 'rg', 'ag', 'iab'], parent__isnull=False, - session__scheduledsession__schedule=self).distinct().order_by('parent__acronym', 'acronym') + return Group.objects.filter(type__slug__in=['wg', 'rg', 'ag', 'iab'], parent__isnull=False, session__scheduledsession__schedule=self).exclude(session__scheduledsession__timeslot__type__in=['lead','offagenda']).distinct().order_by('parent__acronym', 'acronym') # calculate badness of entire schedule def calc_badness(self): @@ -848,7 +867,8 @@ class Session(models.Model): meeting = models.ForeignKey(Meeting) name = models.CharField(blank=True, max_length=255, help_text="Name of session, in case the session has a purpose rather than just being a group meeting.") short = models.CharField(blank=True, max_length=32, help_text="Short version of 'name' above, for use in filenames.") - group = models.ForeignKey(Group) # The group type determines the session type. BOFs also need to be added as a group. + type = models.ForeignKey(TimeSlotTypeName) + group = models.ForeignKey(Group) # The group type historically determined the session type. BOFs also need to be added as a group. Note that not all meeting requests have a natural group to associate with. attendees = models.IntegerField(null=True, blank=True) agenda_note = models.CharField(blank=True, max_length=255) requested = models.DateTimeField(default=datetime.datetime.now) @@ -1004,7 +1024,13 @@ class Session(models.Model): else: return "The agenda has not been uploaded yet." - def type(self): + # FIXME - This used to be called 'type'. It is only used in agenda.csv and agenda.txt. + # It will return the _wrong thing_ if you look back at an agenda of an earlier meeting + # where group X was a BOF at the time, but is now a WG. + # It also doesn't return anything useful for RG, area sessions, or anything that's not group type 'wg'. + # A better thing to do is find a way to note when a meeting was a BoF meeting and use that, removing this + # function altogether. + def lame_description(self): if self.group.type.slug in [ "wg" ]: return "BOF" if self.group.state.slug in ["bof", "bof-conc"] else "WG" else: diff --git a/ietf/meeting/test_data.py b/ietf/meeting/test_data.py index 997d2e4bf..0053eb558 100644 --- a/ietf/meeting/test_data.py +++ b/ietf/meeting/test_data.py @@ -20,6 +20,9 @@ def make_meeting_test_data(): pname = RoomResourceName.objects.create(name='projector',slug='proj') projector = ResourceAssociation.objects.create(name=pname,icon="notfound.png",desc="Basic projector") room = Room.objects.create(meeting=meeting, name="Test Room", capacity=123) + breakfast_room = Room.objects.create(meeting=meeting, name="Breakfast Room", capacity=40) + room.session_types.add("session") + breakfast_room.session_types.add("lead") room.resources = [projector] # mars WG @@ -29,7 +32,7 @@ def make_meeting_test_data(): mars_session = Session.objects.create(meeting=meeting, group=mars, attendees=10, requested_by=system_person, requested_duration=20, status_id="schedw", - scheduled=datetime.datetime.now()) + scheduled=datetime.datetime.now(),type_id="session") mars_session.resources = [projector] ScheduledSession.objects.create(timeslot=slot, session=mars_session, schedule=schedule) @@ -39,26 +42,37 @@ def make_meeting_test_data(): ames_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="ames"), attendees=10, requested_by=system_person, requested_duration=20, status_id="schedw", - scheduled=datetime.datetime.now()) + scheduled=datetime.datetime.now(),type_id="session") ScheduledSession.objects.create(timeslot=slot, session=ames_session, schedule=schedule) + # IESG breakfast + breakfast_slot = TimeSlot.objects.create(meeting=meeting, type_id="lead", duration=90 * 60, + location=breakfast_room, + time=datetime.datetime.combine(datetime.date.today(),datetime.time(7,0))) + iesg_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="iesg"), + name="IESG Breakfast", + attendees=25, requested_by=system_person, + requested_duration=20, status_id="schedw", + scheduled=datetime.datetime.now(),type_id="lead") + ScheduledSession.objects.create(timeslot=breakfast_slot, session=iesg_session, schedule=schedule) + meeting.agenda = schedule meeting.save() - doc = Document.objects.create(name='agenda-mars-ietf-42', type_id='agenda', title="Agenda", external_url="agenda-mars.txt",group=mars) + doc = Document.objects.create(name='agenda-mars-ietf-42', type_id='agenda', title="Agenda", external_url="agenda-mars.txt",group=mars,rev='00') doc.set_state(State.objects.get(type=doc.type_id, slug="active")) mars_session.sessionpresentation_set.add(SessionPresentation(session=mars_session,document=doc,rev=doc.rev)) - doc = Document.objects.create(name='minutes-mars-ietf-42', type_id='minutes', title="Minutes", external_url="minutes-mars.txt",group=mars) + doc = Document.objects.create(name='minutes-mars-ietf-42', type_id='minutes', title="Minutes", external_url="minutes-mars.txt",group=mars,rev='00') doc.set_state(State.objects.get(type=doc.type_id, slug="active")) mars_session.sessionpresentation_set.add(SessionPresentation(session=mars_session,document=doc,rev=doc.rev)) - doc = Document.objects.create(name='slides-mars-ietf-42', type_id='slides', title="Slideshow", external_url="slides-mars.txt",group=mars) + doc = Document.objects.create(name='slides-mars-ietf-42', type_id='slides', title="Slideshow", external_url="slides-mars.txt",group=mars,rev='00') doc.set_state(State.objects.get(type=doc.type_id, slug="active")) doc.set_state(State.objects.get(type='reuse_policy',slug='single')) mars_session.sessionpresentation_set.add(SessionPresentation(session=mars_session,document=doc,rev=doc.rev)) - doc = Document.objects.create(name='slides-mars-ietf-42-deleted', type_id='slides', title="Bad Slideshow", external_url="slides-mars-deleted.txt",group=mars) + doc = Document.objects.create(name='slides-mars-ietf-42-deleted', type_id='slides', title="Bad Slideshow", external_url="slides-mars-deleted.txt",group=mars,rev='00') doc.set_state(State.objects.get(type=doc.type_id, slug="deleted")) doc.set_state(State.objects.get(type='reuse_policy',slug='single')) mars_session.sessionpresentation_set.add(SessionPresentation(session=mars_session,document=doc,rev=doc.rev)) diff --git a/ietf/meeting/tests_api.py b/ietf/meeting/tests_api.py index 489fa8a90..5953c928b 100644 --- a/ietf/meeting/tests_api.py +++ b/ietf/meeting/tests_api.py @@ -138,7 +138,7 @@ class ApiTests(TestCase): timeslots_before = meeting.timeslot_set.count() url = urlreverse("ietf.meeting.ajax.timeslot_roomsurl", kwargs=dict(num=meeting.number)) - post_data = { "name": "new room", "capacity": "50" , "resources": []} + post_data = { "name": "new room", "capacity": "50" , "resources": [], "session_types":["session"]} # unauthorized post r = self.client.post(url, post_data) @@ -152,7 +152,10 @@ class ApiTests(TestCase): self.assertTrue(meeting.room_set.filter(name="new room")) timeslots_after = meeting.timeslot_set.count() - self.assertEqual((timeslots_after - timeslots_before), (meeting.room_set.count() - 1) * timeslots_before) + # It's not clear that what that ajax function is doing is the right thing to do, + # but it currently makes a new timeslot for any existing timeslot. + # The condition tested below relies on the timeslots before this test all having different start and end times + self.assertEqual( timeslots_after, 2 * timeslots_before) def test_delete_room(self): meeting = make_meeting_test_data() @@ -201,7 +204,7 @@ class ApiTests(TestCase): r = self.client.get(url) self.assertEqual(r.status_code, 200) info = json.loads(r.content) - self.assertEqual(set([x['short_name'] for x in info]),set(['mars','ames'])) + self.assertEqual(set([x['short_name'] for x in info]),set([s.session.short_name for s in meeting.agenda.scheduledsession_set.filter(session__type_id='session')])) schedule = meeting.agenda url = urlreverse("ietf.meeting.ajax.scheduledsessions_json", @@ -209,7 +212,7 @@ class ApiTests(TestCase): r = self.client.get(url) self.assertEqual(r.status_code, 200) info = json.loads(r.content) - self.assertEqual(len(info),2) + self.assertEqual(len(info),schedule.scheduledsession_set.count()) def test_slot_json(self): diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index f849499fa..1e48885b3 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -11,7 +11,7 @@ from pyquery import PyQuery from ietf.doc.models import Document from ietf.meeting.models import Session, TimeSlot from ietf.meeting.test_data import make_meeting_test_data -from ietf.utils.test_utils import TestCase +from ietf.utils.test_utils import TestCase, login_testing_unauthorized class MeetingTests(TestCase): def setUp(self): @@ -124,6 +124,38 @@ class MeetingTests(TestCase): self.assertTrue(session.group.acronym in agenda_content) self.assertTrue(slot.location.name in agenda_content) + def test_agenda_by_room(self): + meeting = make_meeting_test_data() + url = urlreverse("ietf.meeting.views.agenda_by_room",kwargs=dict(num=meeting.number)) + login_testing_unauthorized(self,"secretary",url) + r = self.client.get(url) + self.assertTrue(all([x in r.content for x in ['mars','IESG Breakfast','Test Room','Breakfast Room']])) + + def test_agenda_by_type(self): + meeting = make_meeting_test_data() + + url = urlreverse("ietf.meeting.views.agenda_by_type",kwargs=dict(num=meeting.number)) + login_testing_unauthorized(self,"secretary",url) + r = self.client.get(url) + self.assertTrue(all([x in r.content for x in ['mars','IESG Breakfast','Test Room','Breakfast Room']])) + + url = urlreverse("ietf.meeting.views.agenda_by_type",kwargs=dict(num=meeting.number,type='session')) + r = self.client.get(url) + self.assertTrue(all([x in r.content for x in ['mars','Test Room']])) + self.assertFalse(any([x in r.content for x in ['IESG Breakfast','Breakfast Room']])) + + url = urlreverse("ietf.meeting.views.agenda_by_type",kwargs=dict(num=meeting.number,type='lead')) + r = self.client.get(url) + self.assertFalse(any([x in r.content for x in ['mars','Test Room']])) + self.assertTrue(all([x in r.content for x in ['IESG Breakfast','Breakfast Room']])) + + def test_session_details(self): + meeting = make_meeting_test_data() + url = urlreverse("ietf.meeting.views.session_details", kwargs=dict(num=meeting.number, acronym="mars")) + r = self.client.get(url) + self.assertTrue(all([x in r.content for x in ('slides','agenda','minutes')])) + self.assertFalse('deleted' in r.content) + def test_materials(self): meeting = make_meeting_test_data() session = Session.objects.filter(meeting=meeting, group__acronym="mars").first() diff --git a/ietf/meeting/urls.py b/ietf/meeting/urls.py index 424a57aa8..351f3ab5b 100644 --- a/ietf/meeting/urls.py +++ b/ietf/meeting/urls.py @@ -33,6 +33,10 @@ urlpatterns = patterns('', (r'^(?P\d+)/agenda(?P.txt)$', views.agenda), (r'^(?P\d+)/agenda.ics$', views.ical_agenda), (r'^(?P\d+)/agenda(?P.csv)$', views.agenda), + (r'^(?P\d+)/agenda/by-room$', views.agenda_by_room), + (r'^(?P\d+)/agenda/by-type$', views.agenda_by_type), + (r'^(?P\d+)/agenda/by-type/(?P[a-z]+)$', views.agenda_by_type), + (r'^(?P\d+)/agenda/by-type/(?P[a-z]+)/ics$', views.agenda_by_type_ics), (r'^(?P\d+)/agendas/edit$', views.edit_agendas), (r'^(?P\d+)/timeslots/edit$', views.edit_timeslots), (r'^(?P\d+)/rooms$', ajax.timeslot_roomsurl), diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index fd4de3a45..52c8d7562 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -6,6 +6,7 @@ import re import tarfile import urllib from tempfile import mkstemp +from collections import OrderedDict import debug # pyflakes:ignore @@ -258,8 +259,7 @@ def edit_agenda(request, num=None, owner=None, name=None): meeting_base_url = request.build_absolute_uri(meeting.base_url()) site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash - rooms = meeting.room_set.order_by("capacity") - rooms = rooms.all() + rooms = meeting.room_set.filter(session_types__slug='session').distinct().order_by("capacity") saveas = SaveAsForm() saveasurl=reverse(edit_agenda, args=[meeting.number, schedule.owner_email(), schedule.name]) @@ -367,6 +367,7 @@ def edit_agendas(request, num=None, order=None): def agenda(request, num=None, name=None, base=None, ext=None): base = base if base else 'agenda' ext = ext if ext else '.html' + # This is misleading - urls.py doesn't send ics through here anymore mimetype = { ".html":"text/html; charset=%s"%settings.DEFAULT_CHARSET, ".txt": "text/plain; charset=%s"%settings.DEFAULT_CHARSET, @@ -380,7 +381,39 @@ def agenda(request, num=None, name=None, base=None, ext=None): return render(request, "meeting/no-"+base+ext, {'meeting':meeting }, content_type=mimetype[ext]) updated = meeting_updated(meeting) - return render(request, "meeting/"+base+ext, {"schedule":schedule, "updated": updated}, content_type=mimetype[ext]) + filtered_assignments = schedule.assignments.exclude(timeslot__type__in=['lead','offagenda']) + return render(request, "meeting/"+base+ext, {"schedule":schedule, "filtered_assignments":filtered_assignments, "updated": updated}, content_type=mimetype[ext]) + +@role_required('Area Director','Secretariat','IAB') +def agenda_by_room(request,num=None): + meeting = get_meeting(num) + schedule = get_schedule(meeting) + ss_by_day = OrderedDict() + for day in schedule.scheduledsession_set.dates('timeslot__time','day'): + ss_by_day[day]=[] + for ss in schedule.scheduledsession_set.order_by('timeslot__location__functional_name','timeslot__location__name','timeslot__time'): + day = ss.timeslot.time.date() + ss_by_day[day].append(ss) + return render(request,"meeting/agenda_by_room.html",{"meeting":meeting,"ss_by_day":ss_by_day}) + +@role_required('Area Director','Secretariat','IAB') +def agenda_by_type(request,num=None,type=None): + meeting = get_meeting(num) + schedule = get_schedule(meeting) + scheduledsessions = schedule.scheduledsession_set.order_by('session__type__slug','timeslot__time') + if type: + scheduledsessions = scheduledsessions.filter(session__type__slug=type) + return render(request,"meeting/agenda_by_type.html",{"meeting":meeting,"scheduledsessions":scheduledsessions}) + +@role_required('Area Director','Secretariat','IAB') +def agenda_by_type_ics(request,num=None,type=None): + meeting = get_meeting(num) + schedule = get_schedule(meeting) + scheduledsessions = schedule.scheduledsession_set.order_by('session__type__slug','timeslot__time') + if type: + scheduledsessions = scheduledsessions.filter(session__type__slug=type) + updated = meeting_updated(meeting) + return render(request,"meeting/agenda.ics",{"schedule":schedule,"updated":updated,"assignments":scheduledsessions},content_type="text/calendar") def read_agenda_file(num, doc): # XXXX FIXME: the path fragment in the code below should be moved to @@ -611,7 +644,7 @@ def ical_agenda(request, num=None, name=None, ext=None): elif item[0] == '~': include_types |= set([item[1:]]) - assignments = schedule.assignments.filter( + assignments = schedule.assignments.exclude(timeslot__type__in=['lead','offagenda']).filter( Q(timeslot__type__slug__in = include_types) | Q(session__group__acronym__in = include) | Q(session__group__parent__acronym__in = include) @@ -626,7 +659,7 @@ def ical_agenda(request, num=None, name=None, ext=None): def meeting_requests(request, num=None) : meeting = get_meeting(num) - sessions = Session.objects.filter(meeting__number=meeting.number,group__parent__isnull = False).exclude(requested_by=0).order_by("group__parent__acronym","status__slug","group__acronym") + sessions = Session.objects.filter(meeting__number=meeting.number, type__slug='session', group__parent__isnull = False).exclude(requested_by=0).order_by("group__parent__acronym","status__slug","group__acronym") groups_not_meeting = Group.objects.filter(state='Active',type__in=['WG','RG','BOF']).exclude(acronym__in = [session.group.acronym for session in sessions]).order_by("parent__acronym","acronym") @@ -637,7 +670,7 @@ def meeting_requests(request, num=None) : def session_details(request, num, acronym, date=None, week_day=None, seq=None) : meeting = get_meeting(num) - sessions = Session.objects.filter(meeting=meeting,group__acronym=acronym) + sessions = Session.objects.filter(meeting=meeting,group__acronym=acronym,type__in=['session','plenary','other']) if not sessions: sessions = Session.objects.filter(meeting=meeting,short=acronym) @@ -684,11 +717,14 @@ def session_details(request, num, acronym, date=None, week_day=None, seq=None) : ss = session.scheduledsession_set.filter(schedule=meeting.agenda).order_by('timeslot__time') if ss: scheduled_time = ','.join([x.timeslot.time.strftime("%A %b-%d %H%M") for x in ss]) + # TODO FIXME Deleted materials shouldn't be in the sessionpresentation_set + filtered_sessionpresentation_set = [p for p in session.sessionpresentation_set.all() if p.document.get_state_slug(p.document.type_id)!='deleted'] return render(request, "meeting/session_details.html", { 'session':sessions[0] , 'meeting' :meeting , 'acronym' :acronym, 'time': scheduled_time, + 'filtered_sessionpresentation_set': filtered_sessionpresentation_set }) else: return render(request, "meeting/session_list.html", diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json index f6dc6373c..8aad8add6 100644 --- a/ietf/name/fixtures/names.json +++ b/ietf/name/fixtures/names.json @@ -1414,7 +1414,7 @@ "order": 0, "used": true, "name": "None Selected", - "desc": "" + "desc": "[None selected]" }, "model": "name.iprlicensetypename", "pk": "none-selected" @@ -2079,6 +2079,26 @@ "model": "name.timeslottypename", "pk": "reserved" }, +{ + "fields": { + "order": 0, + "used": true, + "name": "Leadership", + "desc": "Leadership Meetings" + }, + "model": "name.timeslottypename", + "pk": "lead" +}, +{ + "fields": { + "order": 0, + "used": true, + "name": "Off Agenda", + "desc": "Other Meetings Not Published on Agenda" + }, + "model": "name.timeslottypename", + "pk": "offagenda" +}, { "fields": { "label": "State" diff --git a/ietf/name/migrations/0004_auto_20150318_1140.py b/ietf/name/migrations/0004_auto_20150318_1140.py new file mode 100644 index 000000000..c4274b886 --- /dev/null +++ b/ietf/name/migrations/0004_auto_20150318_1140.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations + + + + +def make_new_slot_types(apps, schema_editor): + + TimeSlotTypeName = apps.get_model("name","TimeSlotTypeName") + TimeSlotTypeName.objects.create(slug='lead',name='Leadership',desc='Leadership Meetings',used=True) + TimeSlotTypeName.objects.create(slug='offagenda',name='Off Agenda',desc='Other Meetings Not Published on Agenda',used=True) + +class Migration(migrations.Migration): + + dependencies = [ + ('name', '0003_fix_ipr_none_selected_choice'), + ] + + operations = [ + migrations.RunPython(make_new_slot_types) + ] diff --git a/ietf/secr/meetings/forms.py b/ietf/secr/meetings/forms.py index d3ee39e2e..72a7774f1 100644 --- a/ietf/secr/meetings/forms.py +++ b/ietf/secr/meetings/forms.py @@ -179,7 +179,7 @@ class TimeSlotForm(forms.Form): class NonSessionForm(TimeSlotForm): short = forms.CharField(max_length=32,label='Short Name',help_text='Enter an abbreviated session name (used for material file names)',required=False) - type = forms.ModelChoiceField(queryset=TimeSlotTypeName.objects.filter(slug__in=('other','reg','break','plenary')),empty_label=None) + type = forms.ModelChoiceField(queryset=TimeSlotTypeName.objects.filter(used=True).exclude(slug__in=('session',)),empty_label=None) group = forms.ModelChoiceField(queryset=Group.objects.filter(acronym__in=('edu','ietf','iepg','tools','iesg','iab','iaoc')),help_text='Required for Session types: other, plenary',required=False) show_location = forms.BooleanField(required=False) diff --git a/ietf/secr/meetings/tests.py b/ietf/secr/meetings/tests.py index ddba8a2e5..5e8eb6bbe 100644 --- a/ietf/secr/meetings/tests.py +++ b/ietf/secr/meetings/tests.py @@ -147,21 +147,21 @@ class MainTestCase(TestCase): response = self.client.get(url) self.assertEqual(response.status_code, 200) q = PyQuery(response.content) - self.assertEqual(len(q("#id_rooms_table tr")),2) + self.assertEqual(len(q("#id_rooms_table tr input[type='checkbox']")),meeting.room_set.count()) # test delete # first unschedule sessions so we can delete ScheduledSession.objects.filter(schedule=meeting.agenda).delete() self.client.login(username="secretary", password="secretary+password") - response = self.client.post(url, { + post_dict = { 'room-TOTAL_FORMS': q('input[name="room-TOTAL_FORMS"]').val(), 'room-INITIAL_FORMS': q('input[name="room-INITIAL_FORMS"]').val(), - 'room-0-meeting': q('input[name="room-0-meeting"]').val(), - 'room-0-id': q('input[name="room-0-id"]').val(), - 'room-0-name': q('input[name="room-0-name"]').val(), - 'room-0-capacity': q('input[name="room-0-capacity"]').val(), - 'room-0-DELETE': 'on' - }) + } + for i in range(meeting.room_set.count()): + for attr in ['meeting','id','name','capacity','DELETE']: + key = 'room-%d-%s' % (i,attr) + post_dict[key] = q('input[name="%s"]' % key).val() + response = self.client.post(url, post_dict) self.assertEqual(response.status_code, 302) self.assertEqual(Room.objects.filter(meeting=meeting).count(),0) @@ -176,6 +176,7 @@ class MainTestCase(TestCase): meeting = make_meeting_test_data() qs = TimeSlot.objects.filter(meeting=meeting,type='session') before = qs.count() + expected_deletion_count = qs.filter(time=qs.first().time).count() url = reverse('meetings_times_delete',kwargs={ 'meeting_id':42, 'schedule_name':'test-agenda', @@ -185,7 +186,7 @@ class MainTestCase(TestCase): response = self.client.get(url) self.assertEqual(response.status_code, 302) after = TimeSlot.objects.filter(meeting=meeting,type='session').count() - self.assertEqual(after,before - (Room.objects.filter(meeting=meeting).count())) + self.assertEqual(after,before - expected_deletion_count) def test_meetings_times_edit(self): meeting = make_meeting_test_data() diff --git a/ietf/secr/meetings/views.py b/ietf/secr/meetings/views.py index 1b2e7eed9..7a66eb622 100644 --- a/ietf/secr/meetings/views.py +++ b/ietf/secr/meetings/views.py @@ -53,10 +53,15 @@ def build_timeslots(meeting,room=None): or adding a room. ''' slots = meeting.timeslot_set.filter(type='session') + + # Don't do anything if the room is not capable of handling sessions + if room and not room.session_types.filter(slug='session'): + return + if room: rooms = [room] else: - rooms = meeting.room_set.all() + rooms = meeting.room_set.filter(session_types__slug='session') if not slots or room: # if we are just building timeslots for a new room, the room argument was passed, # then we need to use current meeting times as a template, not the last meeting times @@ -95,7 +100,7 @@ def build_nonsession(meeting,schedule): system = Person.objects.get(name='(system)') secretariat = Group.objects.get(acronym='secretariat') - for slot in TimeSlot.objects.filter(meeting=last_meeting,type__in=('break','reg','other','plenary')): + for slot in TimeSlot.objects.filter(meeting=last_meeting,type__in=('break','reg','other','plenary','lead')): new_time = slot.time + delta session = None # create Session object for Tutorials to hold materials @@ -105,13 +110,17 @@ def build_nonsession(meeting,schedule): short=get_session(slot).short, group=get_session(slot).group, requested_by=system, - status_id='sched') + status_id='sched', + type=slot.type, + ) else: session, __ = Session.objects.get_or_create(meeting=meeting, name=slot.name, group=secretariat, requested_by=system, - status_id='sched') + status_id='sched', + type=slot.type, + ) session.save() ts = TimeSlot.objects.create(type=slot.type, @@ -128,15 +137,17 @@ def check_nonsession(meeting,schedule): Ensure non-session timeslots exist and have appropriate ScheduledSession objects for the specified schedule. ''' - slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary')) + slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary','lead','offagenda')) if not slots: build_nonsession(meeting,schedule) return None - scheduledsessions = slots.filter(type='plenary').first().scheduledsession_set.all() - if not scheduledsessions.filter(schedule=schedule): - source = scheduledsessions.first().schedule - copy_scheduledsessions(slots,source,schedule) + plenary = slots.filter(type='plenary').first() + if plenary: + scheduledsessions = plenary.scheduledsession_set.all() + if not scheduledsessions.filter(schedule=schedule): + source = scheduledsessions.first().schedule + copy_scheduledsessions(slots,source,schedule) def copy_scheduledsessions(slots,source,target): ''' @@ -435,7 +446,7 @@ def non_session(request, meeting_id, schedule_name): check_nonsession(meeting,schedule) - slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary')).order_by('-type__name','time') + slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary','lead')).order_by('-type__name','time') if request.method == 'POST': form = NonSessionForm(request.POST) @@ -458,7 +469,7 @@ def non_session(request, meeting_id, schedule_name): duration=duration, show_location=form.cleaned_data['show_location']) - if timeslot.type.slug not in ('other','plenary'): + if timeslot.type.slug not in ('other','plenary','lead'): group = Group.objects.get(acronym='secretariat') # create associated Session object @@ -467,7 +478,9 @@ def non_session(request, meeting_id, schedule_name): short=short, group=group, requested_by=Person.objects.get(name='(system)'), - status_id='sched') + status_id='sched', + type=type, + ) session.save() # create association @@ -501,7 +514,7 @@ def non_session_delete(request, meeting_id, schedule_name, slot_id): meeting = get_object_or_404(Meeting, number=meeting_id) # schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name) slot = get_object_or_404(TimeSlot, id=slot_id) - if slot.type_id in ('other','plenary'): + if slot.type_id in ('other','plenary','lead'): scheduledsessions = slot.scheduledsession_set.filter(schedule__meeting=meeting) session_objects = [ x.session for x in scheduledsessions ] for session in session_objects: diff --git a/ietf/secr/proceedings/tests.py b/ietf/secr/proceedings/tests.py index e70258956..640d43188 100644 --- a/ietf/secr/proceedings/tests.py +++ b/ietf/secr/proceedings/tests.py @@ -75,7 +75,7 @@ class BluesheetTestCase(TestCase): make_test_data() meeting = Meeting.objects.filter(type='interim').first() group = Group.objects.get(acronym='mars') - Session.objects.create(meeting=meeting,group=group,requested_by_id=1,status_id='sched') + Session.objects.create(meeting=meeting,group=group,requested_by_id=1,status_id='sched',type_id='session') url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':'mars'}) upfile = StringIO('dummy file') upfile.name = "scan1.pdf" diff --git a/ietf/secr/proceedings/views.py b/ietf/secr/proceedings/views.py index 9e38f0aff..aadc8253d 100644 --- a/ietf/secr/proceedings/views.py +++ b/ietf/secr/proceedings/views.py @@ -438,7 +438,9 @@ def interim(request, acronym): Session.objects.create(meeting=meeting, group=group, requested_by=request.user.person, - status_id='sched') + status_id='sched', + type_id='session', + ) create_interim_directory() make_directories(meeting) diff --git a/ietf/secr/sreq/views.py b/ietf/secr/sreq/views.py index 92ce65e74..292fff87f 100644 --- a/ietf/secr/sreq/views.py +++ b/ietf/secr/sreq/views.py @@ -270,7 +270,9 @@ def confirm(request, acronym): requested_by=login, requested_duration=datetime.timedelta(0,int(duration)), comments=form['comments'], - status=SessionStatusName.objects.get(slug=slug)) + status=SessionStatusName.objects.get(slug=slug), + type_id='session', + ) session_save(new_session) if 'resources' in form: new_session.resources = form['resources'] @@ -383,7 +385,9 @@ def edit_mtg(request, num, acronym): requested_by=login, requested_duration=duration, comments=form.cleaned_data['comments'], - status=SessionStatusName.objects.get(slug='schedw')) + status=SessionStatusName.objects.get(slug='schedw'), + type_id='session', + ) new_session.save() else: duration = datetime.timedelta(0,int(form.cleaned_data['length_session2'])) @@ -405,7 +409,9 @@ def edit_mtg(request, num, acronym): requested_by=login, requested_duration=duration, comments=form.cleaned_data['comments'], - status=SessionStatusName.objects.get(slug='apprw')) + status=SessionStatusName.objects.get(slug='apprw'), + type_id='session', + ) new_session.save() else: duration = datetime.timedelta(0,int(form.cleaned_data['length_session3'])) diff --git a/ietf/templates/meeting/agenda.csv b/ietf/templates/meeting/agenda.csv index c1563f6bc..93f78720a 100644 --- a/ietf/templates/meeting/agenda.csv +++ b/ietf/templates/meeting/agenda.csv @@ -1,7 +1,7 @@ {% load humanize %}{% autoescape off %}{% load ietf_filters %}"Date","Start","End","Session","Room","Area","Acronym","Type","Description","Session ID","Agenda","Slides" -{% for item in schedule.assignments.all.distinct %}{% if item.timeslot.type.slug == "break" %}"{{ item.timeslot.time|date:"Y-m-d" }}","{{ item.timeslot.time_desc|slice:":4" }}","{{ item.timeslot.time_desc|slice:"5:9" }}","Break","{{ schedule.meeting.break_area}}","","","","{{ item.timeslot.name }}","b{{ item.timeslot.pk }}","","" +{% for item in filtered_assignments.all.distinct %}{% if item.timeslot.type.slug == "break" %}"{{ item.timeslot.time|date:"Y-m-d" }}","{{ item.timeslot.time_desc|slice:":4" }}","{{ item.timeslot.time_desc|slice:"5:9" }}","Break","{{ schedule.meeting.break_area}}","","","","{{ item.timeslot.name }}","b{{ item.timeslot.pk }}","","" {% endif %}{% if item.timeslot.type.slug == "reg" %}"{{ item.timeslot.time|date:"Y-m-d" }}","{{ item.timeslot.time_desc|slice:":4" }}","{{ item.timeslot.time_desc|slice:"5:9" }}","{{ item.timeslot.type.name }}","{{ schedule.meeting.reg_area }}","","","","{{ item.timeslot.name }}","r{{item.timeslot.pk}}","","" {% endif %}{% if item.timeslot.type.slug == "other" %}"{{ item.timeslot.time|date:"Y-m-d" }}","{{ item.timeslot.time_desc|slice:":4" }}","{{ item.timeslot.time_desc|slice:"5:9" }}","None","{{ item.timeslot.location.name }}","","{{ item.session.group.acronym }}","{% if item.session.group.parent %}{{item.session.group.parent.acronym|upper}}{% endif %}","{{ item.session.name }}","{{item.session.pk}}","","" {% endif %}{% if item.timeslot.type.slug == "plenary" %}"{{ item.timeslot.time|date:"Y-m-d" }}","{{ item.timeslot.time_desc|slice:":4" }}","{{ item.timeslot.time_desc|slice:"5:9" }}","{{ item.session.name }}","{{ item.timeslot.location.name }}","","{{ item.session.group.acronym }}","","{{ item.session.name }}","{{item.session.pk}}","{% if item.session.agenda %}http://www.ietf.org/proceedings/{{ schedule.meeting.number }}/agenda/{{ item.session.agenda.external_url }}{% endif %}","{% if item.session.slides %}{% for slide in item.session.slides %}http://www.ietf.org/proceedings/{{ schedule.meeting.number }}/slides/{{ slide.external_url }}{% if not forloop.last %}|{% endif %}{% endfor %}{% endif %}" -{% endif %}{% if item.timeslot.type.slug == "session" and item.session.group %}"{{ item.timeslot.time|date:"Y-m-d" }}","{{ item.timeslot.time_desc|slice:":4" }}","{{ item.timeslot.time_desc|slice:"5:9" }}","{{ item.timeslot.name }}","{{ item.timeslot.location.name }}","{{ item.session.group.parent.acronym|upper }}","{{ item.session.group.acronym }}","{{ item.session.type }}","{{ item.session.group.name }}","{{ item.session.pk}}","{% if item.session.agenda %}http://www.ietf.org/proceedings/{{ schedule.meeting.number }}/agenda/{{ item.session.agenda.external_url }}{% endif %}","{% if item.session.slides %}{% for slide in item.session.slides %}http://www.ietf.org/proceedings/{{ schedule.meeting.number }}/slides/{{ slide.external_url }}{% if not forloop.last %}|{% endif %}{% endfor %}{% endif %}" -{% endif %}{% endfor %}{% endautoescape %} \ No newline at end of file +{% endif %}{% if item.timeslot.type.slug == "session" and item.session.group %}"{{ item.timeslot.time|date:"Y-m-d" }}","{{ item.timeslot.time_desc|slice:":4" }}","{{ item.timeslot.time_desc|slice:"5:9" }}","{{ item.timeslot.name }}","{{ item.timeslot.location.name }}","{{ item.session.group.parent.acronym|upper }}","{{ item.session.group.acronym }}","{{ item.session.lame_description }}","{{ item.session.group.name }}","{{ item.session.pk}}","{% if item.session.agenda %}http://www.ietf.org/proceedings/{{ schedule.meeting.number }}/agenda/{{ item.session.agenda.external_url }}{% endif %}","{% if item.session.slides %}{% for slide in item.session.slides %}http://www.ietf.org/proceedings/{{ schedule.meeting.number }}/slides/{{ slide.external_url }}{% if not forloop.last %}|{% endif %}{% endfor %}{% endif %}" +{% endif %}{% endfor %}{% endautoescape %} diff --git a/ietf/templates/meeting/agenda.html b/ietf/templates/meeting/agenda.html index 1983ae1d0..95bb014a4 100644 --- a/ietf/templates/meeting/agenda.html +++ b/ietf/templates/meeting/agenda.html @@ -146,7 +146,7 @@ - {% for item in schedule.assignments.all %} + {% for item in filtered_assignments.all %} {% ifchanged item.timeslot.time|date:"Y-m-d" %} @@ -335,7 +335,7 @@
+ {% for ss in day.list %} + + + + + + {% endfor %} +
{{ss.timeslot.time|date:"H:i"}}-{{ss.timeslot.end_time|date:"H:i"}}{{ss.timeslot.get_hidden_location}}{{ss.session.short_name}}
+ + {% endfor %} + + +{% endfor %} + +{% endblock %} diff --git a/ietf/templates/meeting/landscape_edit.html b/ietf/templates/meeting/landscape_edit.html index 916a602f9..4cfe34a58 100644 --- a/ietf/templates/meeting/landscape_edit.html +++ b/ietf/templates/meeting/landscape_edit.html @@ -110,6 +110,8 @@ promiselist.push(ss_promise);

Please save this agenda to your account first.

+
{% comment %} For preserving styling across the facelift {% endcomment %} +
@@ -206,10 +208,11 @@ promiselist.push(ss_promise);
- +{%comment%}
{%endcomment%} +
- + {% comment %}{% endcomment %} {% for day in time_slices %} - + {% comment %}{% endcomment %} {% for day in time_slices %} {% for slot in date_slices|lookup:day %} @@ -246,6 +249,7 @@ promiselist.push(ss_promise);
X
{{r.name}} ({{r.capacity}})
+{% comment 'The secretariat is not using these features' %} +{% endcomment %} {% for day in time_slices %} {% for slot in date_slices|lookup:day %} @@ -372,4 +377,6 @@ promiselist.push(ss_promise); + {% comment %} End of .content div {% endcomment %} + {% endblock %} diff --git a/ietf/templates/meeting/session_details.html b/ietf/templates/meeting/session_details.html index 68cc580b9..fe86627c6 100644 --- a/ietf/templates/meeting/session_details.html +++ b/ietf/templates/meeting/session_details.html @@ -12,11 +12,11 @@

{{ session.name }}

{% endif %} - {% if session.sessionpresentation_set.all.count %} + {% if filtered_sessionpresentation_set %}

Materials:

diff --git a/static/css/agenda/agenda.css b/static/css/agenda/agenda.css index 44d419395..3b5f40959 100644 --- a/static/css/agenda/agenda.css +++ b/static/css/agenda/agenda.css @@ -18,6 +18,8 @@ .content table { border-collapse: separate; border-spacing: 2px; } +.container-fluid {padding-right: 0; padding-left:0;} + .ietf-navbar { background-color: #edf5ff; padding:0; border: 1px solid #89d; margin-top:4px; width: 130px; } .ietf-navbar ul { padding: 0; margin: 0; } .ietf-navbar ul li { list-style: none; padding: 0; margin: 0; font-size: 93%; padding: 2px 0px 2px 0px; } @@ -69,6 +71,7 @@ background-color:#2647A0; width:100px; /* otherwise we get overlap */ min-width:100px; + min-height:40px; font-size:12px; position:absolute; /* makes it float independent of scroll */ z-index:100; /* meeting events will be on top otherwise */ @@ -242,7 +245,7 @@ tr.agenda_slot{ td.agenda_slot{ /* overflow-y:auto; */ /* height:20px; */ - height: 40px; + height: 45px; /* min-height:20px; */ } diff --git a/static/js/agenda/agenda_objects.js b/static/js/agenda/agenda_objects.js index 1cc85af09..7f2f728b7 100644 --- a/static/js/agenda/agenda_objects.js +++ b/static/js/agenda/agenda_objects.js @@ -1172,6 +1172,10 @@ Session.prototype.event_template = function() { } //console.log("acronym", groupacronym, this.group.acronym, this.visible_title()); + var durationstring=""; + if (this.requested_duration!="0.0") { + durationstring = " ("+this.requested_duration+")" + } // see comment in ietf.ccs, and // http://stackoverflow.com/questions/5148041/does-firefox-support-position-relative-on-table-elements return "
x
@@ -226,7 +229,7 @@ promiselist.push(ss_promise);
{{slot.0|date:'Hi'}}-{{slot.1|date:'Hi'}}
{% for resource in r.resources.all %} @@ -255,6 +259,7 @@ promiselist.push(ss_promise); {% endfor %}
"+ this.visible_title()+ - " ("+this.requested_duration+")" + + "" + durationstring + "" + ""+pinned+"
"+ area_mark +"
"; };