Introduce support for setting a base schedule on a schedule. All
assignments on the base schedule are shown in the pages for the schedule, read-only. This allows managing things like breaks and misc sessions separately from the regular WG sessions. Base schedules are not allowed to be the base of other base schedules (the hierarchy can only be one level deep) to simplify the mental model and the code. Add link for creating new schedules instead of relying on copying Empty-Schedule and change the meeting creation code to no longer create the special Empty-Schedule. Instead a "base" schedule is created and a first schedule with the name and permissions of the user creating the meeting, using "base" as base. Speed up a couple of the Secretariat/AD agenda views by adding prefetches. - Legacy-Id: 18355
This commit is contained in:
parent
923cb35755
commit
c78ffbcd18
|
@ -33,11 +33,11 @@ for meeting in Meeting.objects.filter(type="ietf").order_by("date"):
|
||||||
for schedule in meeting.schedule_set.all():
|
for schedule in meeting.schedule_set.all():
|
||||||
print " Checking for missing Break and Reg sessions in %s" % schedule
|
print " Checking for missing Break and Reg sessions in %s" % schedule
|
||||||
for timeslot in meeting.timeslot_set.all():
|
for timeslot in meeting.timeslot_set.all():
|
||||||
if timeslot.type_id == 'break':
|
if timeslot.type_id == 'break' and not (schedule.base and SchedTimeSessAssignment.objects.filter(timeslot=timeslot, session=brk, schedule=schedule.base).exists()):
|
||||||
assignment, created = ScheduleTimeslotSSessionAssignment.objects.get_or_create(timeslot=timeslot, session=brk, schedule=schedule)
|
assignment, created = SchedTimeSessAssignment.objects.get_or_create(timeslot=timeslot, session=brk, schedule=schedule)
|
||||||
if created:
|
if created:
|
||||||
print " Added %s break assignment" % timeslot
|
print " Added %s break assignment" % timeslot
|
||||||
if timeslot.type_id == 'reg':
|
if timeslot.type_id == 'reg' and not (schedule.base and SchedTimeSessAssignment.objects.filter(timeslot=timeslot, session=reg, schedule=schedule.base).exists()):
|
||||||
assignment, created = ScheduleTimeslotSSessionAssignment.objects.get_or_create(timeslot=timeslot, session=reg, schedule=schedule)
|
assignment, created = SchedTimeSessAssignment.objects.get_or_create(timeslot=timeslot, session=reg, schedule=schedule)
|
||||||
if created:
|
if created:
|
||||||
print " Added %s registration assignment" % timeslot
|
print " Added %s registration assignment" % timeslot
|
||||||
|
|
|
@ -126,8 +126,8 @@ admin.site.register(SchedulingEvent, SchedulingEventAdmin)
|
||||||
|
|
||||||
class ScheduleAdmin(admin.ModelAdmin):
|
class ScheduleAdmin(admin.ModelAdmin):
|
||||||
list_display = ["name", "meeting", "owner", "visible", "public", "badness"]
|
list_display = ["name", "meeting", "owner", "visible", "public", "badness"]
|
||||||
list_filter = ["meeting", ]
|
list_filter = ["meeting"]
|
||||||
raw_id_fields = ["meeting", "owner", ]
|
raw_id_fields = ["meeting", "owner", "origin", "base"]
|
||||||
search_fields = ["meeting__number", "name", "owner__name"]
|
search_fields = ["meeting__number", "name", "owner__name"]
|
||||||
ordering = ["-meeting", "name"]
|
ordering = ["-meeting", "name"]
|
||||||
|
|
||||||
|
|
|
@ -143,13 +143,6 @@ def get_schedule(meeting, name=None):
|
||||||
schedule = get_object_or_404(meeting.schedule_set, name=name)
|
schedule = get_object_or_404(meeting.schedule_set, name=name)
|
||||||
return schedule
|
return schedule
|
||||||
|
|
||||||
def get_schedule_by_id(meeting, schedid):
|
|
||||||
if schedid is None:
|
|
||||||
schedule = meeting.schedule
|
|
||||||
else:
|
|
||||||
schedule = get_object_or_404(meeting.schedule_set, id=int(schedid))
|
|
||||||
return schedule
|
|
||||||
|
|
||||||
# seems this belongs in ietf/person/utils.py?
|
# seems this belongs in ietf/person/utils.py?
|
||||||
def get_person_by_email(email):
|
def get_person_by_email(email):
|
||||||
# email == None may actually match people who haven't set an email!
|
# email == None may actually match people who haven't set an email!
|
||||||
|
@ -428,6 +421,11 @@ def get_announcement_initial(meeting, is_change=False):
|
||||||
type = group.type.slug.upper()
|
type = group.type.slug.upper()
|
||||||
if group.type.slug == 'wg' and group.state.slug == 'bof':
|
if group.type.slug == 'wg' and group.state.slug == 'bof':
|
||||||
type = 'BOF'
|
type = 'BOF'
|
||||||
|
|
||||||
|
assignments = SchedTimeSessAssignment.objects.filter(
|
||||||
|
schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None]
|
||||||
|
).order_by('timeslot__time')
|
||||||
|
|
||||||
initial['subject'] = '{name} ({acronym}) {type} {desc} Meeting: {date}{change}'.format(
|
initial['subject'] = '{name} ({acronym}) {type} {desc} Meeting: {date}{change}'.format(
|
||||||
name=group.name,
|
name=group.name,
|
||||||
acronym=group.acronym,
|
acronym=group.acronym,
|
||||||
|
|
|
@ -85,8 +85,11 @@ class Command(BaseCommand):
|
||||||
date=datetime.date(2019, 11, 16),
|
date=datetime.date(2019, 11, 16),
|
||||||
days=7,
|
days=7,
|
||||||
)
|
)
|
||||||
schedule = Schedule.objects.create(meeting=m, name='Empty-Schedule', owner_id=1,
|
base_schedule = Schedule.objects.create(meeting=m, name='base', owner_id=1,
|
||||||
visible=True, public=True)
|
visible=True, public=True)
|
||||||
|
|
||||||
|
schedule = Schedule.objects.create(meeting=m, name='first1', owner_id=1,
|
||||||
|
visible=True, public=True, base=base_schedule)
|
||||||
m.schedule = schedule
|
m.schedule = schedule
|
||||||
m.save()
|
m.save()
|
||||||
|
|
||||||
|
|
25
ietf/meeting/migrations/0032_add_schedule_base.py
Normal file
25
ietf/meeting/migrations/0032_add_schedule_base.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Generated by Django 2.0.13 on 2020-08-07 09:30
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import django.db.models.deletion
|
||||||
|
import ietf.utils.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('meeting', '0031_add_session_origin'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='schedule',
|
||||||
|
name='base',
|
||||||
|
field=ietf.utils.models.ForeignKey(blank=True, help_text='Sessions scheduled in the base show up in this schedule.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='derivedschedule_set', to='meeting.Schedule'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='schedule',
|
||||||
|
name='origin',
|
||||||
|
field=ietf.utils.models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='meeting.Schedule'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -291,7 +291,9 @@ class Meeting(models.Model):
|
||||||
min_time = datetime.datetime(1970, 1, 1, 0, 0, 0) # should be Meeting.modified, but we don't have that
|
min_time = datetime.datetime(1970, 1, 1, 0, 0, 0) # should be Meeting.modified, but we don't have that
|
||||||
timeslots_updated = self.timeslot_set.aggregate(Max('modified'))["modified__max"] or min_time
|
timeslots_updated = self.timeslot_set.aggregate(Max('modified'))["modified__max"] or min_time
|
||||||
sessions_updated = self.session_set.aggregate(Max('modified'))["modified__max"] or min_time
|
sessions_updated = self.session_set.aggregate(Max('modified'))["modified__max"] or min_time
|
||||||
assignments_updated = (self.schedule.assignments.aggregate(Max('modified'))["modified__max"] or min_time) if self.schedule else min_time
|
assignments_updated = min_time
|
||||||
|
if self.schedule:
|
||||||
|
assignments_updated = SchedTimeSessAssignment.objects.filter(schedule__in=[self.schedule, self.schedule.base if self.schedule else None]).aggregate(Max('modified'))["modified__max"] or min_time
|
||||||
ts = max(timeslots_updated, sessions_updated, assignments_updated)
|
ts = max(timeslots_updated, sessions_updated, assignments_updated)
|
||||||
tz = pytz.timezone(settings.PRODUCTION_TIMEZONE)
|
tz = pytz.timezone(settings.PRODUCTION_TIMEZONE)
|
||||||
ts = tz.localize(ts)
|
ts = tz.localize(ts)
|
||||||
|
@ -450,7 +452,7 @@ class TimeSlot(models.Model):
|
||||||
@property
|
@property
|
||||||
def session(self):
|
def session(self):
|
||||||
if not hasattr(self, "_session_cache"):
|
if not hasattr(self, "_session_cache"):
|
||||||
self._session_cache = self.sessions.filter(timeslotassignments__schedule=self.meeting.schedule).first()
|
self._session_cache = self.sessions.filter(timeslotassignments__schedule__in=[self.meeting.schedule, self.meeting.schedule.base if self.meeting else None]).first()
|
||||||
return self._session_cache
|
return self._session_cache
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -626,7 +628,10 @@ class Schedule(models.Model):
|
||||||
public = models.BooleanField(default=True, help_text="Allow others to see this agenda.")
|
public = models.BooleanField(default=True, help_text="Allow others to see this agenda.")
|
||||||
badness = models.IntegerField(null=True, blank=True)
|
badness = models.IntegerField(null=True, blank=True)
|
||||||
notes = models.TextField(blank=True)
|
notes = models.TextField(blank=True)
|
||||||
origin = ForeignKey('Schedule', blank=True, null=True, on_delete=models.SET_NULL)
|
origin = ForeignKey('Schedule', blank=True, null=True, on_delete=models.SET_NULL, related_name="+")
|
||||||
|
base = ForeignKey('Schedule', blank=True, null=True, on_delete=models.SET_NULL,
|
||||||
|
help_text="Sessions scheduled in the base schedule show up in this schedule too.", related_name="derivedschedule_set",
|
||||||
|
limit_choices_to={'base': None}) # prevent the inheritance from being more than one layer deep (no recursion)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u"%s:%s(%s)" % (self.meeting, self.name, self.owner)
|
return u"%s:%s(%s)" % (self.meeting, self.name, self.owner)
|
||||||
|
@ -1047,7 +1052,7 @@ class Session(models.Model):
|
||||||
ss0name = "(%s)" % SessionStatusName.objects.get(slug=status_id).name
|
ss0name = "(%s)" % SessionStatusName.objects.get(slug=status_id).name
|
||||||
else:
|
else:
|
||||||
ss0name = "(unscheduled)"
|
ss0name = "(unscheduled)"
|
||||||
ss = self.timeslotassignments.filter(schedule=self.meeting.schedule).order_by('timeslot__time')
|
ss = self.timeslotassignments.filter(schedule__in=[self.meeting.schedule, self.meeting.schedule.base if self.meeting.schedule else None]).order_by('timeslot__time')
|
||||||
if ss:
|
if ss:
|
||||||
ss0name = ','.join(x.timeslot.time.strftime("%a-%H%M") for x in ss)
|
ss0name = ','.join(x.timeslot.time.strftime("%a-%H%M") for x in ss)
|
||||||
return "%s: %s %s %s" % (self.meeting, self.group.acronym, self.name, ss0name)
|
return "%s: %s %s %s" % (self.meeting, self.group.acronym, self.name, ss0name)
|
||||||
|
@ -1080,11 +1085,8 @@ class Session(models.Model):
|
||||||
def reverse_constraints(self):
|
def reverse_constraints(self):
|
||||||
return Constraint.objects.filter(target=self.group, meeting=self.meeting).order_by('name__name')
|
return Constraint.objects.filter(target=self.group, meeting=self.meeting).order_by('name__name')
|
||||||
|
|
||||||
def timeslotassignment_for_schedule(self, schedule):
|
|
||||||
return self.timeslotassignments.filter(schedule=schedule).first()
|
|
||||||
|
|
||||||
def official_timeslotassignment(self):
|
def official_timeslotassignment(self):
|
||||||
return self.timeslotassignment_for_schedule(self.meeting.schedule)
|
return self.timeslotassignments.filter(schedule__in=[self.meeting.schedule, self.meeting.schedule.base if self.meeting.schedule else None]).first()
|
||||||
|
|
||||||
def constraints_dict(self, host_scheme):
|
def constraints_dict(self, host_scheme):
|
||||||
constraint_list = []
|
constraint_list = []
|
||||||
|
|
|
@ -76,8 +76,9 @@ def make_meeting_test_data(meeting=None):
|
||||||
|
|
||||||
if not meeting:
|
if not meeting:
|
||||||
meeting = Meeting.objects.get(number="72", type="ietf")
|
meeting = Meeting.objects.get(number="72", type="ietf")
|
||||||
schedule = Schedule.objects.create(meeting=meeting, owner=plainman, name="test-schedule", visible=True, public=True)
|
base_schedule = Schedule.objects.create(meeting=meeting, owner=plainman, name="base", visible=True, public=True)
|
||||||
unofficial_schedule = Schedule.objects.create(meeting=meeting, owner=plainman, name="test-unofficial-schedule", visible=True, public=True)
|
schedule = Schedule.objects.create(meeting=meeting, owner=plainman, name="test-schedule", visible=True, public=True, base=base_schedule)
|
||||||
|
unofficial_schedule = Schedule.objects.create(meeting=meeting, owner=plainman, name="test-unofficial-schedule", visible=True, public=True, base=base_schedule)
|
||||||
|
|
||||||
# test room
|
# test room
|
||||||
pname = RoomResourceName.objects.create(name='projector',slug='proj')
|
pname = RoomResourceName.objects.create(name='projector',slug='proj')
|
||||||
|
@ -146,7 +147,7 @@ def make_meeting_test_data(meeting=None):
|
||||||
requested_duration=datetime.timedelta(minutes=480),
|
requested_duration=datetime.timedelta(minutes=480),
|
||||||
type_id="reg")
|
type_id="reg")
|
||||||
SchedulingEvent.objects.create(session=reg_session, status_id='schedw', by=system_person)
|
SchedulingEvent.objects.create(session=reg_session, status_id='schedw', by=system_person)
|
||||||
SchedTimeSessAssignment.objects.create(timeslot=reg_slot, session=reg_session, schedule=schedule)
|
SchedTimeSessAssignment.objects.create(timeslot=reg_slot, session=reg_session, schedule=base_schedule)
|
||||||
|
|
||||||
# Break
|
# Break
|
||||||
break_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="secretariat"),
|
break_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="secretariat"),
|
||||||
|
@ -154,7 +155,7 @@ def make_meeting_test_data(meeting=None):
|
||||||
requested_duration=datetime.timedelta(minutes=30),
|
requested_duration=datetime.timedelta(minutes=30),
|
||||||
type_id="break")
|
type_id="break")
|
||||||
SchedulingEvent.objects.create(session=break_session, status_id='schedw', by=system_person)
|
SchedulingEvent.objects.create(session=break_session, status_id='schedw', by=system_person)
|
||||||
SchedTimeSessAssignment.objects.create(timeslot=break_slot, session=break_session, schedule=schedule)
|
SchedTimeSessAssignment.objects.create(timeslot=break_slot, session=break_session, schedule=base_schedule)
|
||||||
|
|
||||||
meeting.schedule = schedule
|
meeting.schedule = schedule
|
||||||
meeting.save()
|
meeting.save()
|
||||||
|
|
|
@ -133,6 +133,8 @@ class EditMeetingScheduleTests(IetfLiveServerTestCase):
|
||||||
url = self.absreverse('ietf.meeting.views.edit_meeting_schedule', kwargs=dict(num=meeting.number, name=schedule.name, owner=schedule.owner_email()))
|
url = self.absreverse('ietf.meeting.views.edit_meeting_schedule', kwargs=dict(num=meeting.number, name=schedule.name, owner=schedule.owner_email()))
|
||||||
self.driver.get(url)
|
self.driver.get(url)
|
||||||
|
|
||||||
|
WebDriverWait(self.driver, 2).until(expected_conditions.presence_of_element_located((By.CSS_SELECTOR, '.edit-meeting-schedule')))
|
||||||
|
|
||||||
self.assertEqual(len(self.driver.find_elements_by_css_selector('.session')), 3)
|
self.assertEqual(len(self.driver.find_elements_by_css_selector('.session')), 3)
|
||||||
|
|
||||||
# select - show session info
|
# select - show session info
|
||||||
|
|
|
@ -127,6 +127,8 @@ class MeetingTests(TestCase):
|
||||||
future_meeting = Meeting.objects.create(date=datetime.date(future_year, 7, 22), number=future_num, type_id='ietf',
|
future_meeting = Meeting.objects.create(date=datetime.date(future_year, 7, 22), number=future_num, type_id='ietf',
|
||||||
city="Panama City", country="PA", time_zone='America/Panama')
|
city="Panama City", country="PA", time_zone='America/Panama')
|
||||||
|
|
||||||
|
registration_text = "Registration"
|
||||||
|
|
||||||
# utc
|
# utc
|
||||||
time_interval = "%s-%s" % (slot.utc_start_time().strftime("%H:%M").lstrip("0"), (slot.utc_start_time() + slot.duration).strftime("%H:%M").lstrip("0"))
|
time_interval = "%s-%s" % (slot.utc_start_time().strftime("%H:%M").lstrip("0"), (slot.utc_start_time() + slot.duration).strftime("%H:%M").lstrip("0"))
|
||||||
|
|
||||||
|
@ -152,6 +154,7 @@ class MeetingTests(TestCase):
|
||||||
self.assertIn(session.group.parent.acronym.upper(), agenda_content)
|
self.assertIn(session.group.parent.acronym.upper(), agenda_content)
|
||||||
self.assertIn(slot.location.name, agenda_content)
|
self.assertIn(slot.location.name, agenda_content)
|
||||||
self.assertIn(time_interval, agenda_content)
|
self.assertIn(time_interval, agenda_content)
|
||||||
|
self.assertIn(registration_text, agenda_content)
|
||||||
|
|
||||||
# Make sure there's a frame for the agenda and it points to the right place
|
# Make sure there's a frame for the agenda and it points to the right place
|
||||||
self.assertTrue(any([session.materials.get(type='agenda').get_href() in x.attrib["data-src"] for x in q('tr div.modal-body div.frame')]))
|
self.assertTrue(any([session.materials.get(type='agenda').get_href() in x.attrib["data-src"] for x in q('tr div.modal-body div.frame')]))
|
||||||
|
@ -191,6 +194,7 @@ class MeetingTests(TestCase):
|
||||||
self.assertContains(r, session.group.name)
|
self.assertContains(r, session.group.name)
|
||||||
self.assertContains(r, session.group.parent.acronym.upper())
|
self.assertContains(r, session.group.parent.acronym.upper())
|
||||||
self.assertContains(r, slot.location.name)
|
self.assertContains(r, slot.location.name)
|
||||||
|
self.assertContains(r, registration_text)
|
||||||
|
|
||||||
self.assertContains(r, session.materials.get(type='agenda').uploaded_filename)
|
self.assertContains(r, session.materials.get(type='agenda').uploaded_filename)
|
||||||
self.assertContains(r, session.materials.filter(type='slides').exclude(states__type__slug='slides',states__slug='deleted').first().uploaded_filename)
|
self.assertContains(r, session.materials.filter(type='slides').exclude(states__type__slug='slides',states__slug='deleted').first().uploaded_filename)
|
||||||
|
@ -215,6 +219,7 @@ class MeetingTests(TestCase):
|
||||||
self.assertNotContains(r, 'CANCELLED')
|
self.assertNotContains(r, 'CANCELLED')
|
||||||
self.assertContains(r, session.group.acronym)
|
self.assertContains(r, session.group.acronym)
|
||||||
self.assertContains(r, slot.location.name)
|
self.assertContains(r, slot.location.name)
|
||||||
|
self.assertContains(r, registration_text)
|
||||||
|
|
||||||
# week view with a cancelled session
|
# week view with a cancelled session
|
||||||
SchedulingEvent.objects.create(
|
SchedulingEvent.objects.create(
|
||||||
|
@ -585,16 +590,22 @@ class MeetingTests(TestCase):
|
||||||
self.client.login(username='secretary',password='secretary+password')
|
self.client.login(username='secretary',password='secretary+password')
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code,200)
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
new_base = Schedule.objects.create(name="newbase", owner=schedule.owner, meeting=schedule.meeting)
|
||||||
response = self.client.post(url, {
|
response = self.client.post(url, {
|
||||||
'name':schedule.name,
|
'name':schedule.name,
|
||||||
'visible':True,
|
'visible':True,
|
||||||
'public':True,
|
'public':True,
|
||||||
|
'notes': "New Notes",
|
||||||
|
'base': new_base.pk,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code,302)
|
self.assertNoFormPostErrors(response)
|
||||||
schedule = Schedule.objects.get(pk=schedule.pk)
|
schedule.refresh_from_db()
|
||||||
self.assertTrue(schedule.visible)
|
self.assertTrue(schedule.visible)
|
||||||
self.assertTrue(schedule.public)
|
self.assertTrue(schedule.public)
|
||||||
|
self.assertEqual(schedule.notes, "New Notes")
|
||||||
|
self.assertEqual(schedule.base_id, new_base.pk)
|
||||||
|
|
||||||
def test_agenda_by_type_ics(self):
|
def test_agenda_by_type_ics(self):
|
||||||
session=SessionFactory(meeting__type_id='ietf',type_id='lead')
|
session=SessionFactory(meeting__type_id='ietf',type_id='lead')
|
||||||
|
@ -985,7 +996,21 @@ class EditTests(TestCase):
|
||||||
person=p,
|
person=p,
|
||||||
name=ConstraintName.objects.get(slug="bethere"),
|
name=ConstraintName.objects.get(slug="bethere"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
room = Room.objects.get(meeting=meeting, session_types='regular')
|
||||||
|
base_timeslot = TimeSlot.objects.create(meeting=meeting, type_id='regular', location=room,
|
||||||
|
duration=datetime.timedelta(minutes=50),
|
||||||
|
time=datetime.datetime.combine(meeting.date + datetime.timedelta(days=2), datetime.time(9, 30)))
|
||||||
|
|
||||||
|
timeslots = list(TimeSlot.objects.filter(meeting=meeting, type='regular').order_by('time'))
|
||||||
|
|
||||||
|
base_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="irg"),
|
||||||
|
attendees=20, requested_duration=datetime.timedelta(minutes=30),
|
||||||
|
type_id='regular')
|
||||||
|
SchedulingEvent.objects.create(session=base_session, status_id='schedw', by=Person.objects.get(user__username='secretary'))
|
||||||
|
SchedTimeSessAssignment.objects.create(timeslot=base_timeslot, session=base_session, schedule=meeting.schedule.base)
|
||||||
|
|
||||||
|
|
||||||
# check we have the grid and everything set up as a baseline -
|
# check we have the grid and everything set up as a baseline -
|
||||||
# the Javascript tests check that the Javascript can work with
|
# the Javascript tests check that the Javascript can work with
|
||||||
# it
|
# it
|
||||||
|
@ -993,11 +1018,9 @@ class EditTests(TestCase):
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
q = PyQuery(r.content)
|
q = PyQuery(r.content)
|
||||||
|
|
||||||
room = Room.objects.get(meeting=meeting, session_types='regular')
|
|
||||||
self.assertTrue(q(".room-name:contains(\"{}\")".format(room.name)))
|
self.assertTrue(q(".room-name:contains(\"{}\")".format(room.name)))
|
||||||
self.assertTrue(q(".room-name:contains(\"{}\")".format(room.capacity)))
|
self.assertTrue(q(".room-name:contains(\"{}\")".format(room.capacity)))
|
||||||
|
|
||||||
timeslots = list(TimeSlot.objects.filter(meeting=meeting, type='regular'))
|
|
||||||
self.assertTrue(q("#timeslot{}".format(timeslots[0].pk)))
|
self.assertTrue(q("#timeslot{}".format(timeslots[0].pk)))
|
||||||
|
|
||||||
for s in [s1, s2]:
|
for s in [s1, s2]:
|
||||||
|
@ -1031,12 +1054,14 @@ class EditTests(TestCase):
|
||||||
if s.comments:
|
if s.comments:
|
||||||
self.assertIn(s.comments, e.find(".comments").text())
|
self.assertIn(s.comments, e.find(".comments").text())
|
||||||
|
|
||||||
formatted_constraints = e.find(".session-info .formatted-constraints > *")
|
formatted_constraints1 = q("#session{} .session-info .formatted-constraints > *".format(s1.pk))
|
||||||
if s == s1:
|
self.assertIn(s2.group.acronym, formatted_constraints1.eq(0).html())
|
||||||
self.assertIn(s_other.group.acronym, formatted_constraints.eq(0).html())
|
self.assertIn(p.name, formatted_constraints1.eq(1).html())
|
||||||
self.assertIn(p.name, formatted_constraints.eq(1).html())
|
|
||||||
elif s == s2:
|
formatted_constraints2 = q("#session{} .session-info .formatted-constraints > *".format(s2.pk))
|
||||||
self.assertIn(p.name, formatted_constraints.eq(0).html())
|
self.assertIn(p.name, formatted_constraints2.eq(0).html())
|
||||||
|
|
||||||
|
self.assertEqual(len(q("#session{}.readonly".format(base_session.pk))), 1)
|
||||||
|
|
||||||
self.assertTrue(q("em:contains(\"You can't edit this schedule\")"))
|
self.assertTrue(q("em:contains(\"You can't edit this schedule\")"))
|
||||||
|
|
||||||
|
@ -1106,7 +1131,7 @@ class EditTests(TestCase):
|
||||||
self.assertEqual(tombstone_event.status_id, 'resched')
|
self.assertEqual(tombstone_event.status_id, 'resched')
|
||||||
|
|
||||||
self.assertEqual(SchedTimeSessAssignment.objects.get(schedule=schedule, session=s_tombstone).timeslot, timeslots[1])
|
self.assertEqual(SchedTimeSessAssignment.objects.get(schedule=schedule, session=s_tombstone).timeslot, timeslots[1])
|
||||||
self.assertTrue(PyQuery(json_content['tombstone'])("#session{}.tombstone".format(s_tombstone.pk)).html())
|
self.assertTrue(PyQuery(json_content['tombstone'])("#session{}.readonly".format(s_tombstone.pk)).html())
|
||||||
|
|
||||||
# unassign
|
# unassign
|
||||||
r = self.client.post(url, {
|
r = self.client.post(url, {
|
||||||
|
@ -1117,16 +1142,9 @@ class EditTests(TestCase):
|
||||||
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s1)), [])
|
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s1)), [])
|
||||||
|
|
||||||
# try swapping days
|
# try swapping days
|
||||||
timeslots.append(TimeSlot.objects.create(
|
SchedTimeSessAssignment.objects.create(schedule=schedule, session=s1, timeslot=timeslots[0])
|
||||||
meeting=meeting, type_id='regular', location=timeslots[0].location,
|
self.assertEqual(len(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s1, timeslot=timeslots[0])), 1)
|
||||||
duration=timeslots[0].duration - datetime.timedelta(minutes=5),
|
self.assertEqual(len(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s2, timeslot=timeslots[1])), 1)
|
||||||
time=timeslots[0].time + datetime.timedelta(days=1),
|
|
||||||
))
|
|
||||||
|
|
||||||
SchedTimeSessAssignment.objects.create(schedule=schedule, session=s1, timeslot=timeslots[1])
|
|
||||||
|
|
||||||
self.assertEqual(len(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s2, timeslot=timeslots[0])), 1)
|
|
||||||
self.assertEqual(len(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s1, timeslot=timeslots[1])), 1)
|
|
||||||
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[2])), [])
|
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[2])), [])
|
||||||
|
|
||||||
r = self.client.post(url, {
|
r = self.client.post(url, {
|
||||||
|
@ -1138,8 +1156,8 @@ class EditTests(TestCase):
|
||||||
|
|
||||||
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[0])), [])
|
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[0])), [])
|
||||||
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[1])), [])
|
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[1])), [])
|
||||||
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s1)), [])
|
self.assertEqual(len(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s1, timeslot=timeslots[2])), 1)
|
||||||
self.assertEqual(len(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s2, timeslot=timeslots[2])), 1)
|
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s2)), [])
|
||||||
|
|
||||||
# swap back
|
# swap back
|
||||||
r = self.client.post(url, {
|
r = self.client.post(url, {
|
||||||
|
@ -1149,37 +1167,59 @@ class EditTests(TestCase):
|
||||||
})
|
})
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
|
|
||||||
self.assertEqual(len(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s2, timeslot=timeslots[0])), 1)
|
self.assertEqual(len(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s1, timeslot=timeslots[0])), 1)
|
||||||
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[1])), [])
|
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[1])), [])
|
||||||
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[2])), [])
|
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, timeslot=timeslots[2])), [])
|
||||||
|
|
||||||
def test_copy_meeting_schedule(self):
|
def test_new_meeting_schedule(self):
|
||||||
meeting = make_meeting_test_data()
|
meeting = make_meeting_test_data()
|
||||||
|
|
||||||
self.client.login(username="secretary", password="secretary+password")
|
self.client.login(username="secretary", password="secretary+password")
|
||||||
|
|
||||||
url = urlreverse("ietf.meeting.views.copy_meeting_schedule", kwargs=dict(num=meeting.number, owner=meeting.schedule.owner_email(), name=meeting.schedule.name))
|
# new from scratch
|
||||||
|
url = urlreverse("ietf.meeting.views.new_meeting_schedule", kwargs=dict(num=meeting.number))
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
# copy
|
|
||||||
r = self.client.post(url, {
|
r = self.client.post(url, {
|
||||||
'name': "newtest",
|
'name': "scratch",
|
||||||
'public': "on",
|
'public': "on",
|
||||||
'notes': "New test",
|
'visible': "on",
|
||||||
|
'notes': "New scratch",
|
||||||
|
'base': meeting.schedule.base_id,
|
||||||
})
|
})
|
||||||
self.assertNoFormPostErrors(r)
|
self.assertNoFormPostErrors(r)
|
||||||
|
|
||||||
new_schedule = Schedule.objects.get(meeting=meeting, owner__user__username='secretary', name='newtest')
|
new_schedule = Schedule.objects.get(meeting=meeting, owner__user__username='secretary', name='scratch')
|
||||||
|
self.assertEqual(new_schedule.public, True)
|
||||||
|
self.assertEqual(new_schedule.visible, True)
|
||||||
|
self.assertEqual(new_schedule.notes, "New scratch")
|
||||||
|
self.assertEqual(new_schedule.origin, None)
|
||||||
|
self.assertEqual(new_schedule.base_id, meeting.schedule.base_id)
|
||||||
|
|
||||||
|
# copy
|
||||||
|
url = urlreverse("ietf.meeting.views.new_meeting_schedule", kwargs=dict(num=meeting.number, owner=meeting.schedule.owner_email(), name=meeting.schedule.name))
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
r = self.client.post(url, {
|
||||||
|
'name': "copy",
|
||||||
|
'public': "on",
|
||||||
|
'notes': "New copy",
|
||||||
|
'base': meeting.schedule.base_id,
|
||||||
|
})
|
||||||
|
self.assertNoFormPostErrors(r)
|
||||||
|
|
||||||
|
new_schedule = Schedule.objects.get(meeting=meeting, owner__user__username='secretary', name='copy')
|
||||||
self.assertEqual(new_schedule.public, True)
|
self.assertEqual(new_schedule.public, True)
|
||||||
self.assertEqual(new_schedule.visible, False)
|
self.assertEqual(new_schedule.visible, False)
|
||||||
self.assertEqual(new_schedule.notes, "New test")
|
self.assertEqual(new_schedule.notes, "New copy")
|
||||||
self.assertEqual(new_schedule.origin, meeting.schedule)
|
self.assertEqual(new_schedule.origin, meeting.schedule)
|
||||||
|
self.assertEqual(new_schedule.base_id, meeting.schedule.base_id)
|
||||||
|
|
||||||
old_assignments = {(a.session_id, a.timeslot_id) for a in SchedTimeSessAssignment.objects.filter(schedule=meeting.schedule)}
|
old_assignments = {(a.session_id, a.timeslot_id) for a in SchedTimeSessAssignment.objects.filter(schedule=meeting.schedule)}
|
||||||
for a in SchedTimeSessAssignment.objects.filter(schedule=new_schedule):
|
for a in SchedTimeSessAssignment.objects.filter(schedule=new_schedule):
|
||||||
self.assertIn((a.session_id, a.timeslot_id), old_assignments)
|
self.assertIn((a.session_id, a.timeslot_id), old_assignments)
|
||||||
# FIXME: test extendedfrom is copied correctly
|
|
||||||
|
|
||||||
def test_save_agenda_as_and_read_permissions(self):
|
def test_save_agenda_as_and_read_permissions(self):
|
||||||
meeting = make_meeting_test_data()
|
meeting = make_meeting_test_data()
|
||||||
|
@ -1390,7 +1430,7 @@ class SessionDetailsTests(TestCase):
|
||||||
class EditScheduleListTests(TestCase):
|
class EditScheduleListTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.mtg = MeetingFactory(type_id='ietf')
|
self.mtg = MeetingFactory(type_id='ietf')
|
||||||
ScheduleFactory(meeting=self.mtg,name='Empty-Schedule')
|
ScheduleFactory(meeting=self.mtg, name='secretary1')
|
||||||
|
|
||||||
def test_list_schedules(self):
|
def test_list_schedules(self):
|
||||||
url = urlreverse('ietf.meeting.views.list_schedules',kwargs={'num':self.mtg.number})
|
url = urlreverse('ietf.meeting.views.list_schedules',kwargs={'num':self.mtg.number})
|
||||||
|
@ -1423,8 +1463,8 @@ class EditScheduleListTests(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
# copy
|
# copy
|
||||||
copy_url = urlreverse("ietf.meeting.views.copy_meeting_schedule", kwargs=dict(num=meeting.number, owner=from_schedule.owner_email(), name=from_schedule.name))
|
new_url = urlreverse("ietf.meeting.views.new_meeting_schedule", kwargs=dict(num=meeting.number, owner=from_schedule.owner_email(), name=from_schedule.name))
|
||||||
r = self.client.post(copy_url, {
|
r = self.client.post(new_url, {
|
||||||
'name': "newtest",
|
'name': "newtest",
|
||||||
'public': "on",
|
'public': "on",
|
||||||
})
|
})
|
||||||
|
@ -1436,22 +1476,20 @@ class EditScheduleListTests(TestCase):
|
||||||
|
|
||||||
edit_url = urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs=dict(num=meeting.number, owner=to_schedule.owner_email(), name=to_schedule.name))
|
edit_url = urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs=dict(num=meeting.number, owner=to_schedule.owner_email(), name=to_schedule.name))
|
||||||
|
|
||||||
# schedule
|
# schedule session
|
||||||
r = self.client.post(edit_url, {
|
r = self.client.post(edit_url, {
|
||||||
'action': 'assign',
|
'action': 'assign',
|
||||||
'timeslot': slot3.pk,
|
'timeslot': slot3.pk,
|
||||||
'session': session3.pk,
|
'session': session3.pk,
|
||||||
})
|
})
|
||||||
self.assertEqual(json.loads(r.content)['success'], True)
|
self.assertEqual(json.loads(r.content)['success'], True)
|
||||||
|
# unschedule session
|
||||||
# unschedule
|
|
||||||
r = self.client.post(edit_url, {
|
r = self.client.post(edit_url, {
|
||||||
'action': 'unassign',
|
'action': 'unassign',
|
||||||
'session': session1.pk,
|
'session': session1.pk,
|
||||||
})
|
})
|
||||||
self.assertEqual(json.loads(r.content)['success'], True)
|
self.assertEqual(json.loads(r.content)['success'], True)
|
||||||
|
# move session
|
||||||
# move
|
|
||||||
r = self.client.post(edit_url, {
|
r = self.client.post(edit_url, {
|
||||||
'action': 'assign',
|
'action': 'assign',
|
||||||
'timeslot': slot2.pk,
|
'timeslot': slot2.pk,
|
||||||
|
@ -1459,7 +1497,7 @@ class EditScheduleListTests(TestCase):
|
||||||
})
|
})
|
||||||
self.assertEqual(json.loads(r.content)['success'], True)
|
self.assertEqual(json.loads(r.content)['success'], True)
|
||||||
|
|
||||||
# get differences
|
# now get differences
|
||||||
r = self.client.get(url, {
|
r = self.client.get(url, {
|
||||||
'from_schedule': from_schedule.name,
|
'from_schedule': from_schedule.name,
|
||||||
'to_schedule': to_schedule.name,
|
'to_schedule': to_schedule.name,
|
||||||
|
@ -1584,6 +1622,7 @@ class InterimTests(TestCase):
|
||||||
meeting = add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', group__acronym='mars')).filter(current_status='apprw').first().meeting
|
meeting = add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', group__acronym='mars')).filter(current_status='apprw').first().meeting
|
||||||
meeting.time_zone = 'America/Los_Angeles'
|
meeting.time_zone = 'America/Los_Angeles'
|
||||||
meeting.save()
|
meeting.save()
|
||||||
|
|
||||||
url = urlreverse("ietf.meeting.views.interim_send_announcement", kwargs={'number': meeting.number})
|
url = urlreverse("ietf.meeting.views.interim_send_announcement", kwargs={'number': meeting.number})
|
||||||
login_testing_unauthorized(self, "secretary", url)
|
login_testing_unauthorized(self, "secretary", url)
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
|
|
|
@ -41,7 +41,7 @@ type_ietf_only_patterns = [
|
||||||
url(r'^agenda/%(owner)s/%(schedule_name)s/session/(?P<assignment_id>\d+).json$' % settings.URL_REGEXPS, ajax.assignment_json),
|
url(r'^agenda/%(owner)s/%(schedule_name)s/session/(?P<assignment_id>\d+).json$' % settings.URL_REGEXPS, ajax.assignment_json),
|
||||||
url(r'^agenda/%(owner)s/%(schedule_name)s/sessions.json$' % settings.URL_REGEXPS, ajax.assignments_json),
|
url(r'^agenda/%(owner)s/%(schedule_name)s/sessions.json$' % settings.URL_REGEXPS, ajax.assignments_json),
|
||||||
url(r'^agenda/%(owner)s/%(schedule_name)s.json$' % settings.URL_REGEXPS, ajax.schedule_infourl),
|
url(r'^agenda/%(owner)s/%(schedule_name)s.json$' % settings.URL_REGEXPS, ajax.schedule_infourl),
|
||||||
url(r'^agenda/%(owner)s/%(schedule_name)s/copy/$' % settings.URL_REGEXPS, views.copy_meeting_schedule),
|
url(r'^agenda/%(owner)s/%(schedule_name)s/new/$' % settings.URL_REGEXPS, views.new_meeting_schedule),
|
||||||
url(r'^agenda/by-room$', views.agenda_by_room),
|
url(r'^agenda/by-room$', views.agenda_by_room),
|
||||||
url(r'^agenda/by-type$', views.agenda_by_type),
|
url(r'^agenda/by-type$', views.agenda_by_type),
|
||||||
url(r'^agenda/by-type/(?P<type>[a-z]+)$', views.agenda_by_type),
|
url(r'^agenda/by-type/(?P<type>[a-z]+)$', views.agenda_by_type),
|
||||||
|
@ -49,6 +49,7 @@ type_ietf_only_patterns = [
|
||||||
url(r'^agendas/list$', views.list_schedules),
|
url(r'^agendas/list$', views.list_schedules),
|
||||||
url(r'^agendas/edit$', RedirectView.as_view(pattern_name='ietf.meeting.views.list_schedules', permanent=True)),
|
url(r'^agendas/edit$', RedirectView.as_view(pattern_name='ietf.meeting.views.list_schedules', permanent=True)),
|
||||||
url(r'^agendas/diff/$', views.diff_schedules),
|
url(r'^agendas/diff/$', views.diff_schedules),
|
||||||
|
url(r'^agenda/new/$', views.new_meeting_schedule),
|
||||||
url(r'^timeslots/edit$', views.edit_timeslots),
|
url(r'^timeslots/edit$', views.edit_timeslots),
|
||||||
url(r'^timeslot/(?P<slot_id>\d+)/edittype$', views.edit_timeslot_type),
|
url(r'^timeslot/(?P<slot_id>\d+)/edittype$', views.edit_timeslot_type),
|
||||||
url(r'^rooms$', ajax.timeslot_roomsurl),
|
url(r'^rooms$', ajax.timeslot_roomsurl),
|
||||||
|
|
|
@ -28,7 +28,7 @@ from ietf.person.models import Person, Email
|
||||||
from ietf.secr.proceedings.proc_utils import import_audio_files
|
from ietf.secr.proceedings.proc_utils import import_audio_files
|
||||||
|
|
||||||
def session_time_for_sorting(session, use_meeting_date):
|
def session_time_for_sorting(session, use_meeting_date):
|
||||||
official_timeslot = TimeSlot.objects.filter(sessionassignments__session=session, sessionassignments__schedule=session.meeting.schedule).first()
|
official_timeslot = TimeSlot.objects.filter(sessionassignments__session=session, sessionassignments__schedule__in=[session.meeting.schedule, session.meeting.schedule.base if session.meeting.schedule else None]).first()
|
||||||
if official_timeslot:
|
if official_timeslot:
|
||||||
return official_timeslot.time
|
return official_timeslot.time
|
||||||
elif use_meeting_date and session.meeting.date:
|
elif use_meeting_date and session.meeting.date:
|
||||||
|
|
|
@ -144,7 +144,7 @@ def materials(request, num=None):
|
||||||
|
|
||||||
sessions = add_event_info_to_session_qs(Session.objects.filter(
|
sessions = add_event_info_to_session_qs(Session.objects.filter(
|
||||||
meeting__number=meeting.number,
|
meeting__number=meeting.number,
|
||||||
timeslotassignments__schedule=schedule
|
timeslotassignments__schedule__in=[schedule, schedule.base if schedule else None]
|
||||||
).distinct().select_related('meeting__schedule', 'group__state', 'group__parent'))
|
).distinct().select_related('meeting__schedule', 'group__state', 'group__parent'))
|
||||||
|
|
||||||
plenaries = sessions.filter(name__icontains='plenary')
|
plenaries = sessions.filter(name__icontains='plenary')
|
||||||
|
@ -297,6 +297,8 @@ def schedule_create(request, num=None, owner=None, name=None):
|
||||||
newschedule = Schedule(name=savedname,
|
newschedule = Schedule(name=savedname,
|
||||||
owner=request.user.person,
|
owner=request.user.person,
|
||||||
meeting=meeting,
|
meeting=meeting,
|
||||||
|
base=schedule.base,
|
||||||
|
origin=schedule,
|
||||||
visible=False,
|
visible=False,
|
||||||
public=False)
|
public=False)
|
||||||
|
|
||||||
|
@ -354,14 +356,15 @@ def edit_timeslots(request, num=None):
|
||||||
"ts_list":ts_list,
|
"ts_list":ts_list,
|
||||||
})
|
})
|
||||||
|
|
||||||
class CopyScheduleForm(forms.ModelForm):
|
class NewScheduleForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Schedule
|
model = Schedule
|
||||||
fields = ['name', 'visible', 'public', 'notes']
|
fields = ['name', 'visible', 'public', 'notes', 'base']
|
||||||
|
|
||||||
def __init__(self, schedule, new_owner, *args, **kwargs):
|
def __init__(self, meeting, schedule, new_owner, *args, **kwargs):
|
||||||
super(CopyScheduleForm, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.meeting = meeting
|
||||||
self.schedule = schedule
|
self.schedule = schedule
|
||||||
self.new_owner = new_owner
|
self.new_owner = new_owner
|
||||||
|
|
||||||
|
@ -370,7 +373,7 @@ class CopyScheduleForm(forms.ModelForm):
|
||||||
name_suggestion = username
|
name_suggestion = username
|
||||||
counter = 2
|
counter = 2
|
||||||
|
|
||||||
existing_names = set(Schedule.objects.filter(meeting=schedule.meeting_id, owner=new_owner).values_list('name', flat=True))
|
existing_names = set(Schedule.objects.filter(meeting=meeting, owner=new_owner).values_list('name', flat=True))
|
||||||
while name_suggestion in existing_names:
|
while name_suggestion in existing_names:
|
||||||
name_suggestion = username + str(counter)
|
name_suggestion = username + str(counter)
|
||||||
counter += 1
|
counter += 1
|
||||||
|
@ -378,55 +381,68 @@ class CopyScheduleForm(forms.ModelForm):
|
||||||
self.fields['name'].initial = name_suggestion
|
self.fields['name'].initial = name_suggestion
|
||||||
self.fields['name'].label = "Name of new agenda"
|
self.fields['name'].label = "Name of new agenda"
|
||||||
|
|
||||||
|
self.fields['base'].queryset = self.fields['base'].queryset.filter(meeting=meeting)
|
||||||
|
|
||||||
|
if schedule:
|
||||||
|
self.fields['visible'].initial = schedule.visible
|
||||||
|
self.fields['public'].initial = schedule.public
|
||||||
|
self.fields['base'].queryset = self.fields['base'].queryset.exclude(pk=schedule.pk)
|
||||||
|
self.fields['base'].initial = schedule.base_id
|
||||||
|
else:
|
||||||
|
base = Schedule.objects.filter(meeting=meeting, name='base').first()
|
||||||
|
if base:
|
||||||
|
self.fields['base'].initial = base.pk
|
||||||
|
|
||||||
def clean_name(self):
|
def clean_name(self):
|
||||||
name = self.cleaned_data.get('name')
|
name = self.cleaned_data.get('name')
|
||||||
if name and Schedule.objects.filter(meeting=self.schedule.meeting_id, owner=self.new_owner, name=name):
|
if name and Schedule.objects.filter(meeting=self.meeting, owner=self.new_owner, name=name):
|
||||||
raise forms.ValidationError("Schedule with this name already exists.")
|
raise forms.ValidationError("Schedule with this name already exists.")
|
||||||
return name
|
return name
|
||||||
|
|
||||||
@role_required('Area Director','Secretariat')
|
@role_required('Area Director','Secretariat')
|
||||||
def copy_meeting_schedule(request, num, owner, name):
|
def new_meeting_schedule(request, num, owner=None, name=None):
|
||||||
meeting = get_meeting(num)
|
meeting = get_meeting(num)
|
||||||
schedule = get_object_or_404(meeting.schedule_set, owner__email__address=owner, name=name)
|
schedule = get_schedule_by_name(meeting, get_person_by_email(owner), name)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = CopyScheduleForm(schedule, request.user.person, request.POST)
|
form = NewScheduleForm(meeting, schedule, request.user.person, request.POST)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
new_schedule = form.save(commit=False)
|
new_schedule = form.save(commit=False)
|
||||||
new_schedule.meeting = schedule.meeting
|
new_schedule.meeting = meeting
|
||||||
new_schedule.owner = request.user.person
|
new_schedule.owner = request.user.person
|
||||||
new_schedule.origin = schedule
|
new_schedule.origin = schedule
|
||||||
new_schedule.save()
|
new_schedule.save()
|
||||||
|
|
||||||
# keep a mapping so that extendedfrom references can be chased
|
if schedule:
|
||||||
old_pk_to_new_pk = {}
|
# keep a mapping so that extendedfrom references can be chased
|
||||||
extendedfroms = {}
|
old_pk_to_new_pk = {}
|
||||||
for assignment in schedule.assignments.all():
|
extendedfroms = {}
|
||||||
extendedfrom_id = assignment.extendedfrom_id
|
for assignment in schedule.assignments.all():
|
||||||
|
extendedfrom_id = assignment.extendedfrom_id
|
||||||
|
|
||||||
# clone by resetting primary key
|
# clone by resetting primary key
|
||||||
old_pk = assignment.pk
|
old_pk = assignment.pk
|
||||||
assignment.pk = None
|
assignment.pk = None
|
||||||
assignment.schedule = new_schedule
|
assignment.schedule = new_schedule
|
||||||
assignment.extendedfrom = None
|
assignment.extendedfrom = None
|
||||||
assignment.save()
|
assignment.save()
|
||||||
|
|
||||||
old_pk_to_new_pk[old_pk] = assignment.pk
|
old_pk_to_new_pk[old_pk] = assignment.pk
|
||||||
if extendedfrom_id is not None:
|
if extendedfrom_id is not None:
|
||||||
extendedfroms[assignment.pk] = extendedfrom_id
|
extendedfroms[assignment.pk] = extendedfrom_id
|
||||||
|
|
||||||
for pk, extendedfrom_id in extendedfroms.values():
|
for pk, extendedfrom_id in extendedfroms.values():
|
||||||
if extendedfrom_id in old_pk_to_new_pk:
|
if extendedfrom_id in old_pk_to_new_pk:
|
||||||
SchedTimeSessAssignment.objects.filter(pk=pk).update(extendedfrom=old_pk_to_new_pk[extendedfrom_id])
|
SchedTimeSessAssignment.objects.filter(pk=pk).update(extendedfrom=old_pk_to_new_pk[extendedfrom_id])
|
||||||
|
|
||||||
# now redirect to this new schedule
|
# now redirect to this new schedule
|
||||||
return redirect(edit_meeting_schedule, meeting.number, new_schedule.owner_email(), new_schedule.name)
|
return redirect(edit_meeting_schedule, meeting.number, new_schedule.owner_email(), new_schedule.name)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
form = CopyScheduleForm(schedule, request.user.person)
|
form = NewScheduleForm(meeting, schedule, request.user.person)
|
||||||
|
|
||||||
return render(request, "meeting/copy_meeting_schedule.html", {
|
return render(request, "meeting/new_meeting_schedule.html", {
|
||||||
'meeting': meeting,
|
'meeting': meeting,
|
||||||
'schedule': schedule,
|
'schedule': schedule,
|
||||||
'form': form,
|
'form': form,
|
||||||
|
@ -462,7 +478,15 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
||||||
"hide_menu": True
|
"hide_menu": True
|
||||||
}, status=403, content_type="text/html")
|
}, status=403, content_type="text/html")
|
||||||
|
|
||||||
assignments = get_all_assignments_from_schedule(schedule)
|
assignments = SchedTimeSessAssignment.objects.filter(
|
||||||
|
schedule__in=[schedule, schedule.base],
|
||||||
|
timeslot__location__isnull=False,
|
||||||
|
session__type='regular',
|
||||||
|
).order_by('timeslot__time','timeslot__name')
|
||||||
|
|
||||||
|
assignments_by_session = defaultdict(list)
|
||||||
|
for a in assignments:
|
||||||
|
assignments_by_session[a.session_id].append(a)
|
||||||
|
|
||||||
rooms = meeting.room_set.filter(session_types__slug='regular').distinct().order_by("capacity")
|
rooms = meeting.room_set.filter(session_types__slug='regular').distinct().order_by("capacity")
|
||||||
|
|
||||||
|
@ -483,7 +507,7 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
||||||
'resources', 'group', 'group__parent', 'group__type', 'joint_with_groups',
|
'resources', 'group', 'group__parent', 'group__type', 'joint_with_groups',
|
||||||
)
|
)
|
||||||
|
|
||||||
timeslots_qs = meeting.timeslot_set.filter(type='regular').prefetch_related('type', 'sessions').order_by('location', 'time', 'name')
|
timeslots_qs = TimeSlot.objects.filter(meeting=meeting, type='regular').prefetch_related('type').order_by('location', 'time', 'name')
|
||||||
|
|
||||||
min_duration = min(t.duration for t in timeslots_qs)
|
min_duration = min(t.duration for t in timeslots_qs)
|
||||||
max_duration = max(t.duration for t in timeslots_qs)
|
max_duration = max(t.duration for t in timeslots_qs)
|
||||||
|
@ -549,7 +573,7 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
||||||
|
|
||||||
s.other_sessions = [s_other for s_other in sessions_for_group.get(s.group_id) if s != s_other]
|
s.other_sessions = [s_other for s_other in sessions_for_group.get(s.group_id) if s != s_other]
|
||||||
|
|
||||||
s.is_tombstone = s.current_status in tombstone_states
|
s.readonly = s.current_status in tombstone_states or any(a.schedule_id != schedule.pk for a in assignments_by_session.get(s.pk, []))
|
||||||
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
@ -622,7 +646,9 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
||||||
source_day = swap_days_form.cleaned_data['source_day']
|
source_day = swap_days_form.cleaned_data['source_day']
|
||||||
target_day = swap_days_form.cleaned_data['target_day']
|
target_day = swap_days_form.cleaned_data['target_day']
|
||||||
|
|
||||||
swap_meeting_schedule_timeslot_assignments(schedule, [ts for ts in timeslots_qs if ts.time.date() == source_day], [ts for ts in timeslots_qs if ts.time.date() == target_day], target_day - source_day)
|
source_timeslots = [ts for ts in timeslots_qs if ts.time.date() == source_day]
|
||||||
|
target_timeslots = [ts for ts in timeslots_qs if ts.time.date() == target_day]
|
||||||
|
swap_meeting_schedule_timeslot_assignments(schedule, source_timeslots, target_timeslots, target_day - source_day)
|
||||||
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
|
||||||
|
@ -670,10 +696,6 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
||||||
ts.session_assignments = []
|
ts.session_assignments = []
|
||||||
timeslots_by_pk = {ts.pk: ts for ts in timeslots_qs}
|
timeslots_by_pk = {ts.pk: ts for ts in timeslots_qs}
|
||||||
|
|
||||||
assignments_by_session = defaultdict(list)
|
|
||||||
for a in assignments:
|
|
||||||
assignments_by_session[a.session_id].append(a)
|
|
||||||
|
|
||||||
unassigned_sessions = []
|
unassigned_sessions = []
|
||||||
for s in sessions:
|
for s in sessions:
|
||||||
assigned = False
|
assigned = False
|
||||||
|
@ -798,7 +820,17 @@ def edit_schedule(request, num=None, owner=None, name=None):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
SchedulePropertiesForm = modelform_factory(Schedule, fields=['name', 'notes', 'visible', 'public'])
|
class SchedulePropertiesForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Schedule
|
||||||
|
fields = ['name', 'notes', 'visible', 'public', 'base']
|
||||||
|
|
||||||
|
def __init__(self, meeting, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.fields['base'].queryset = self.fields['base'].queryset.filter(meeting=meeting)
|
||||||
|
if self.instance.pk is not None:
|
||||||
|
self.fields['base'].queryset = self.fields['base'].queryset.exclude(pk=self.instance.pk)
|
||||||
|
|
||||||
@role_required('Area Director','Secretariat')
|
@role_required('Area Director','Secretariat')
|
||||||
def edit_schedule_properties(request, num, owner, name):
|
def edit_schedule_properties(request, num, owner, name):
|
||||||
|
@ -816,14 +848,14 @@ def edit_schedule_properties(request, num, owner, name):
|
||||||
return HttpResponseForbidden("You may not edit this schedule")
|
return HttpResponseForbidden("You may not edit this schedule")
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = SchedulePropertiesForm(instance=schedule, data=request.POST)
|
form = SchedulePropertiesForm(meeting, instance=schedule, data=request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
if request.GET.get('next'):
|
if request.GET.get('next'):
|
||||||
return HttpResponseRedirect(request.GET.get('next'))
|
return HttpResponseRedirect(request.GET.get('next'))
|
||||||
return redirect('ietf.meeting.views.edit_schedule', num=num, owner=owner, name=name)
|
return redirect('ietf.meeting.views.edit_schedule', num=num, owner=owner, name=name)
|
||||||
else:
|
else:
|
||||||
form = SchedulePropertiesForm(instance=schedule)
|
form = SchedulePropertiesForm(meeting, instance=schedule)
|
||||||
|
|
||||||
return render(request, "meeting/properties_edit.html", {
|
return render(request, "meeting/properties_edit.html", {
|
||||||
"schedule": schedule,
|
"schedule": schedule,
|
||||||
|
@ -842,7 +874,7 @@ def list_schedules(request, num):
|
||||||
|
|
||||||
schedules = Schedule.objects.filter(
|
schedules = Schedule.objects.filter(
|
||||||
meeting=meeting
|
meeting=meeting
|
||||||
).prefetch_related('owner', 'assignments', 'origin', 'origin__assignments').order_by('owner', '-name', '-public').distinct()
|
).prefetch_related('owner', 'assignments', 'origin', 'origin__assignments', 'base').order_by('owner', '-name', '-public').distinct()
|
||||||
if not has_role(request.user, 'Secretariat'):
|
if not has_role(request.user, 'Secretariat'):
|
||||||
schedules = schedules.filter(Q(visible=True) | Q(owner=request.user.person))
|
schedules = schedules.filter(Q(visible=True) | Q(owner=request.user.person))
|
||||||
|
|
||||||
|
@ -859,7 +891,7 @@ def list_schedules(request, num):
|
||||||
if s.origin:
|
if s.origin:
|
||||||
s.changes_from_origin = len(diff_meeting_schedules(s.origin, s))
|
s.changes_from_origin = len(diff_meeting_schedules(s.origin, s))
|
||||||
|
|
||||||
if s.pk == meeting.schedule_id:
|
if s in [meeting.schedule, meeting.schedule.base if meeting.schedule else None]:
|
||||||
official_schedules.append(s)
|
official_schedules.append(s)
|
||||||
elif user_is_person(request.user, s.owner):
|
elif user_is_person(request.user, s.owner):
|
||||||
own_schedules.append(s)
|
own_schedules.append(s)
|
||||||
|
@ -869,13 +901,13 @@ def list_schedules(request, num):
|
||||||
other_private_schedules.append(s)
|
other_private_schedules.append(s)
|
||||||
|
|
||||||
schedule_groups = [
|
schedule_groups = [
|
||||||
("Official Agenda", official_schedules),
|
(official_schedules, False, "Official Agenda"),
|
||||||
("Own Draft Agendas", own_schedules),
|
(own_schedules, True, "Own Draft Agendas"),
|
||||||
("Other Draft Agendas", other_public_schedules),
|
(other_public_schedules, False, "Other Draft Agendas"),
|
||||||
("Other Private Draft Agendas", other_private_schedules),
|
(other_private_schedules, False, "Other Private Draft Agendas"),
|
||||||
]
|
]
|
||||||
|
|
||||||
schedule_groups = [(label, sorted(l, reverse=True, key=lambda s: natural_sort_key(s.name))) for label, l in schedule_groups if l]
|
schedule_groups = [(sorted(l, reverse=True, key=lambda s: natural_sort_key(s.name)), own, *t) for l, own, *t in schedule_groups if l or own]
|
||||||
|
|
||||||
return render(request, "meeting/schedule_list.html", {
|
return render(request, "meeting/schedule_list.html", {
|
||||||
'meeting': meeting,
|
'meeting': meeting,
|
||||||
|
@ -967,7 +999,11 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
|
||||||
return render(request, "meeting/no-"+base+ext, {'meeting':meeting }, content_type=mimetype[ext])
|
return render(request, "meeting/no-"+base+ext, {'meeting':meeting }, content_type=mimetype[ext])
|
||||||
|
|
||||||
updated = meeting.updated()
|
updated = meeting.updated()
|
||||||
filtered_assignments = schedule.assignments.exclude(timeslot__type__in=['lead','offagenda'])
|
filtered_assignments = SchedTimeSessAssignment.objects.filter(
|
||||||
|
schedule__in=[schedule, schedule.base]
|
||||||
|
).exclude(
|
||||||
|
timeslot__type__in=['lead', 'offagenda']
|
||||||
|
)
|
||||||
filtered_assignments = preprocess_assignments_for_agenda(filtered_assignments, meeting)
|
filtered_assignments = preprocess_assignments_for_agenda(filtered_assignments, meeting)
|
||||||
|
|
||||||
if ext == ".csv":
|
if ext == ".csv":
|
||||||
|
@ -1095,10 +1131,15 @@ def agenda_by_room(request, num=None, name=None, owner=None):
|
||||||
else:
|
else:
|
||||||
person = get_person_by_email(owner)
|
person = get_person_by_email(owner)
|
||||||
schedule = get_schedule_by_name(meeting, person, name)
|
schedule = get_schedule_by_name(meeting, person, name)
|
||||||
|
|
||||||
|
assignments = SchedTimeSessAssignment.objects.filter(
|
||||||
|
schedule__in=[schedule, schedule.base if schedule else None]
|
||||||
|
).prefetch_related('timeslot', 'timeslot__location', 'session', 'session__group', 'session__group__parent')
|
||||||
|
|
||||||
ss_by_day = OrderedDict()
|
ss_by_day = OrderedDict()
|
||||||
for day in schedule.assignments.dates('timeslot__time','day'):
|
for day in assignments.dates('timeslot__time','day'):
|
||||||
ss_by_day[day]=[]
|
ss_by_day[day]=[]
|
||||||
for ss in schedule.assignments.order_by('timeslot__location__functional_name','timeslot__location__name','timeslot__time'):
|
for ss in assignments.order_by('timeslot__location__functional_name','timeslot__location__name','timeslot__time'):
|
||||||
day = ss.timeslot.time.date()
|
day = ss.timeslot.time.date()
|
||||||
ss_by_day[day].append(ss)
|
ss_by_day[day].append(ss)
|
||||||
return render(request,"meeting/agenda_by_room.html",{"meeting":meeting,"schedule":schedule,"ss_by_day":ss_by_day})
|
return render(request,"meeting/agenda_by_room.html",{"meeting":meeting,"schedule":schedule,"ss_by_day":ss_by_day})
|
||||||
|
@ -1111,7 +1152,12 @@ def agenda_by_type(request, num=None, type=None, name=None, owner=None):
|
||||||
else:
|
else:
|
||||||
person = get_person_by_email(owner)
|
person = get_person_by_email(owner)
|
||||||
schedule = get_schedule_by_name(meeting, person, name)
|
schedule = get_schedule_by_name(meeting, person, name)
|
||||||
assignments = schedule.assignments.order_by('session__type__slug','timeslot__time','session__group__acronym')
|
assignments = SchedTimeSessAssignment.objects.filter(
|
||||||
|
schedule__in=[schedule, schedule.base if schedule else None]
|
||||||
|
).prefetch_related(
|
||||||
|
'timeslot', 'timeslot__location', 'session', 'session__group', 'session__group__parent'
|
||||||
|
).order_by('session__type__slug','timeslot__time','session__group__acronym')
|
||||||
|
|
||||||
if type:
|
if type:
|
||||||
assignments = assignments.filter(session__type__slug=type)
|
assignments = assignments.filter(session__type__slug=type)
|
||||||
return render(request,"meeting/agenda_by_type.html",{"meeting":meeting,"schedule":schedule,"assignments":assignments})
|
return render(request,"meeting/agenda_by_type.html",{"meeting":meeting,"schedule":schedule,"assignments":assignments})
|
||||||
|
@ -1120,7 +1166,11 @@ def agenda_by_type(request, num=None, type=None, name=None, owner=None):
|
||||||
def agenda_by_type_ics(request,num=None,type=None):
|
def agenda_by_type_ics(request,num=None,type=None):
|
||||||
meeting = get_meeting(num)
|
meeting = get_meeting(num)
|
||||||
schedule = get_schedule(meeting)
|
schedule = get_schedule(meeting)
|
||||||
assignments = schedule.assignments.order_by('session__type__slug','timeslot__time')
|
assignments = SchedTimeSessAssignment.objects.filter(
|
||||||
|
schedule__in=[schedule, schedule.base if schedule else None]
|
||||||
|
).prefetch_related(
|
||||||
|
'timeslot', 'timeslot__location', 'session', 'session__group', 'session__group__parent'
|
||||||
|
).order_by('session__type__slug','timeslot__time')
|
||||||
if type:
|
if type:
|
||||||
assignments = assignments.filter(session__type__slug=type)
|
assignments = assignments.filter(session__type__slug=type)
|
||||||
updated = meeting.updated()
|
updated = meeting.updated()
|
||||||
|
@ -1240,7 +1290,11 @@ def week_view(request, num=None, name=None, owner=None):
|
||||||
if not schedule:
|
if not schedule:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
filtered_assignments = schedule.assignments.exclude(timeslot__type__in=['lead','offagenda'])
|
filtered_assignments = SchedTimeSessAssignment.objects.filter(
|
||||||
|
schedule__in=[schedule, schedule.base]
|
||||||
|
).exclude(
|
||||||
|
timeslot__type__in=['lead','offagenda']
|
||||||
|
)
|
||||||
filtered_assignments = preprocess_assignments_for_agenda(filtered_assignments, meeting)
|
filtered_assignments = preprocess_assignments_for_agenda(filtered_assignments, meeting)
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
|
@ -1309,7 +1363,11 @@ def room_view(request, num=None, name=None, owner=None):
|
||||||
person = get_person_by_email(owner)
|
person = get_person_by_email(owner)
|
||||||
schedule = get_schedule_by_name(meeting, person, name)
|
schedule = get_schedule_by_name(meeting, person, name)
|
||||||
|
|
||||||
assignments = schedule.assignments.all()
|
assignments = SchedTimeSessAssignment.objects.filter(
|
||||||
|
schedule__in=[schedule, schedule.base if schedule else None]
|
||||||
|
).prefetch_related(
|
||||||
|
'timeslot', 'timeslot__location', 'session', 'session__group', 'session__group__parent'
|
||||||
|
)
|
||||||
unavailable = meeting.timeslot_set.filter(type__slug='unavail')
|
unavailable = meeting.timeslot_set.filter(type__slug='unavail')
|
||||||
if not (assignments.exists() or unavailable.exists()):
|
if not (assignments.exists() or unavailable.exists()):
|
||||||
return HttpResponse("No sessions/timeslots available yet")
|
return HttpResponse("No sessions/timeslots available yet")
|
||||||
|
@ -1401,7 +1459,7 @@ def ical_agenda(request, num=None, name=None, acronym=None, session_id=None):
|
||||||
elif len(item) > 1 and item[0] == '~':
|
elif len(item) > 1 and item[0] == '~':
|
||||||
include_types |= set([item[1:]])
|
include_types |= set([item[1:]])
|
||||||
|
|
||||||
assignments = schedule.assignments.exclude(timeslot__type__in=['lead','offagenda'])
|
assignments = SchedTimeSessAssignment.objects.filter(schedule__in=[schedule, schedule.base]).exclude(timeslot__type__in=['lead','offagenda'])
|
||||||
assignments = preprocess_assignments_for_agenda(assignments, meeting)
|
assignments = preprocess_assignments_for_agenda(assignments, meeting)
|
||||||
|
|
||||||
if q:
|
if q:
|
||||||
|
@ -1427,13 +1485,17 @@ def ical_agenda(request, num=None, name=None, acronym=None, session_id=None):
|
||||||
}, content_type="text/calendar")
|
}, content_type="text/calendar")
|
||||||
|
|
||||||
@cache_page(15 * 60)
|
@cache_page(15 * 60)
|
||||||
def json_agenda(request, num=None ):
|
def json_agenda(request, num=None):
|
||||||
meeting = get_meeting(num, type_in=['ietf','interim'])
|
meeting = get_meeting(num, type_in=['ietf','interim'])
|
||||||
|
|
||||||
sessions = []
|
sessions = []
|
||||||
locations = set()
|
locations = set()
|
||||||
parent_acronyms = set()
|
parent_acronyms = set()
|
||||||
assignments = meeting.schedule.assignments.exclude(session__type__in=['lead','offagenda','break','reg'])
|
assignments = SchedTimeSessAssignment.objects.filter(
|
||||||
|
schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None]
|
||||||
|
).exclude(
|
||||||
|
session__type__in=['lead','offagenda','break','reg']
|
||||||
|
)
|
||||||
# Update the assignments with historic information, i.e., valid at the
|
# Update the assignments with historic information, i.e., valid at the
|
||||||
# time of the meeting
|
# time of the meeting
|
||||||
assignments = preprocess_assignments_for_agenda(assignments, meeting, extra_prefetches=[
|
assignments = preprocess_assignments_for_agenda(assignments, meeting, extra_prefetches=[
|
||||||
|
@ -1609,7 +1671,7 @@ def session_details(request, num, acronym):
|
||||||
session.historic_group.historic_parent = None
|
session.historic_group.historic_parent = None
|
||||||
|
|
||||||
session.type_counter = Counter()
|
session.type_counter = Counter()
|
||||||
ss = session.timeslotassignments.filter(schedule=meeting.schedule).order_by('timeslot__time')
|
ss = session.timeslotassignments.filter(schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None]).order_by('timeslot__time')
|
||||||
if ss:
|
if ss:
|
||||||
if meeting.type_id == 'interim' and not (meeting.city or meeting.country):
|
if meeting.type_id == 'interim' and not (meeting.city or meeting.country):
|
||||||
session.times = [ x.timeslot.utc_start_time() for x in ss ]
|
session.times = [ x.timeslot.utc_start_time() for x in ss ]
|
||||||
|
@ -2320,15 +2382,23 @@ def make_schedule_official(request, num, owner, name):
|
||||||
schedule.public = True
|
schedule.public = True
|
||||||
schedule.visible = True
|
schedule.visible = True
|
||||||
schedule.save()
|
schedule.save()
|
||||||
|
if schedule.base and not (schedule.base.public and schedule.base.visible):
|
||||||
|
schedule.base.public = True
|
||||||
|
schedule.base.visible = True
|
||||||
|
schedule.base.save()
|
||||||
meeting.schedule = schedule
|
meeting.schedule = schedule
|
||||||
meeting.save()
|
meeting.save()
|
||||||
return HttpResponseRedirect(reverse('ietf.meeting.views.list_schedules',kwargs={'num':num}))
|
return HttpResponseRedirect(reverse('ietf.meeting.views.list_schedules',kwargs={'num':num}))
|
||||||
|
|
||||||
if not schedule.public:
|
if not schedule.public:
|
||||||
messages.warning(request,"This schedule will be made public as it is made official.")
|
messages.warning(request,"This schedule will be made public as it is made official.")
|
||||||
|
|
||||||
if not schedule.visible:
|
if not schedule.visible:
|
||||||
messages.warning(request,"This schedule will be made visible as it is made official.")
|
messages.warning(request,"This schedule will be made visible as it is made official.")
|
||||||
|
if schedule.base:
|
||||||
|
if not schedule.base.public:
|
||||||
|
messages.warning(request,"The base schedule will be made public as it is made official.")
|
||||||
|
if not schedule.base.visible:
|
||||||
|
messages.warning(request,"The base schedule will be made visible as it is made official.")
|
||||||
|
|
||||||
return render(request, "meeting/make_schedule_official.html",
|
return render(request, "meeting/make_schedule_official.html",
|
||||||
{ 'schedule' : schedule,
|
{ 'schedule' : schedule,
|
||||||
|
@ -2344,12 +2414,14 @@ def delete_schedule(request, num, owner, name):
|
||||||
person = get_person_by_email(owner)
|
person = get_person_by_email(owner)
|
||||||
schedule = get_schedule_by_name(meeting, person, name)
|
schedule = get_schedule_by_name(meeting, person, name)
|
||||||
|
|
||||||
if schedule.name=='Empty-Schedule':
|
# FIXME: we ought to put these checks in a function and only show
|
||||||
return HttpResponseForbidden('You may not delete the default empty schedule')
|
# the delete button if the checks pass
|
||||||
|
|
||||||
if schedule == meeting.schedule:
|
if schedule == meeting.schedule:
|
||||||
return HttpResponseForbidden('You may not delete the official schedule for %s'%meeting)
|
return HttpResponseForbidden('You may not delete the official schedule for %s'%meeting)
|
||||||
|
|
||||||
|
if Schedule.objects.filter(base=schedule).exists():
|
||||||
|
return HttpResponseForbidden('You may not delete a schedule serving as the base for other schedules')
|
||||||
|
|
||||||
if not ( has_role(request.user, 'Secretariat') or person.user == request.user ):
|
if not ( has_role(request.user, 'Secretariat') or person.user == request.user ):
|
||||||
return HttpResponseForbidden("You may not delete other user's schedules")
|
return HttpResponseForbidden("You may not delete other user's schedules")
|
||||||
|
|
||||||
|
@ -2660,10 +2732,12 @@ def interim_request_details(request, number):
|
||||||
return redirect(interim_pending)
|
return redirect(interim_pending)
|
||||||
|
|
||||||
first_session = sessions.first()
|
first_session = sessions.first()
|
||||||
|
assignments = SchedTimeSessAssignment.objects.filter(schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None])
|
||||||
|
|
||||||
return render(request, "meeting/interim_request_details.html", {
|
return render(request, "meeting/interim_request_details.html", {
|
||||||
"meeting": meeting,
|
"meeting": meeting,
|
||||||
"sessions": sessions,
|
"sessions": sessions,
|
||||||
|
"assignments": assignments,
|
||||||
"group": first_session.group,
|
"group": first_session.group,
|
||||||
"requester": session_requested_by(first_session),
|
"requester": session_requested_by(first_session),
|
||||||
"session_status": current_session_status(first_session),
|
"session_status": current_session_status(first_session),
|
||||||
|
@ -2783,10 +2857,10 @@ def upcoming_ical(request):
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
|
|
||||||
# get meetings starting 7 days ago -- we'll filter out sessions in the past further down
|
# get meetings starting 7 days ago -- we'll filter out sessions in the past further down
|
||||||
meetings = data_for_meetings_overview(Meeting.objects.filter(date__gte=today-datetime.timedelta(days=7)).order_by('date'))
|
meetings = data_for_meetings_overview(Meeting.objects.filter(date__gte=today-datetime.timedelta(days=7)).prefetch_related('schedule').order_by('date'))
|
||||||
|
|
||||||
assignments = list(SchedTimeSessAssignment.objects.filter(
|
assignments = list(SchedTimeSessAssignment.objects.filter(
|
||||||
schedule__meeting__schedule=F('schedule'),
|
schedule__in=[m.schedule_id for m in meetings] + [m.schedule.base_id for m in meetings if m.schedule],
|
||||||
session__in=[s.pk for m in meetings for s in m.sessions],
|
session__in=[s.pk for m in meetings for s in m.sessions],
|
||||||
timeslot__time__gte=today,
|
timeslot__time__gte=today,
|
||||||
).order_by(
|
).order_by(
|
||||||
|
@ -2879,7 +2953,7 @@ def proceedings(request, num=None):
|
||||||
sessions = add_event_info_to_session_qs(
|
sessions = add_event_info_to_session_qs(
|
||||||
Session.objects.filter(meeting__number=meeting.number)
|
Session.objects.filter(meeting__number=meeting.number)
|
||||||
).filter(
|
).filter(
|
||||||
Q(timeslotassignments__schedule=schedule) | Q(current_status='notmeet')
|
Q(timeslotassignments__schedule__in=[schedule, schedule.base if schedule else None]) | Q(current_status='notmeet')
|
||||||
).select_related().order_by('-current_status')
|
).select_related().order_by('-current_status')
|
||||||
plenaries = sessions.filter(name__icontains='plenary').exclude(current_status='notmeet')
|
plenaries = sessions.filter(name__icontains='plenary').exclude(current_status='notmeet')
|
||||||
ietf = sessions.filter(group__parent__type__slug = 'area').exclude(group__acronym='edu')
|
ietf = sessions.filter(group__parent__type__slug = 'area').exclude(group__acronym='edu')
|
||||||
|
@ -3101,7 +3175,7 @@ def edit_timeslot_type(request, num, slot_id):
|
||||||
else:
|
else:
|
||||||
form = TimeSlotTypeForm(instance=timeslot)
|
form = TimeSlotTypeForm(instance=timeslot)
|
||||||
|
|
||||||
sessions = timeslot.sessions.filter(timeslotassignments__schedule=meeting.schedule)
|
sessions = timeslot.sessions.filter(timeslotassignments__schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None])
|
||||||
|
|
||||||
return render(request, 'meeting/edit_timeslot_type.html', {'timeslot':timeslot,'form':form,'sessions':sessions})
|
return render(request, 'meeting/edit_timeslot_type.html', {'timeslot':timeslot,'form':form,'sessions':sessions})
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ class SecrMeetingTestCase(TestCase):
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
q = PyQuery(response.content)
|
q = PyQuery(response.content)
|
||||||
self.assertEqual(len(q('#id_schedule_selector option')),3)
|
self.assertEqual({option.get('value') for option in q('#id_schedule_selector option:not([value=""])')}, {'base', 'test-schedule', 'test-unofficial-schedule'})
|
||||||
|
|
||||||
def test_add_meeting(self):
|
def test_add_meeting(self):
|
||||||
"Add Meeting"
|
"Add Meeting"
|
||||||
|
@ -92,6 +92,9 @@ class SecrMeetingTestCase(TestCase):
|
||||||
new_meeting = Meeting.objects.get(number=number)
|
new_meeting = Meeting.objects.get(number=number)
|
||||||
|
|
||||||
self.assertTrue(new_meeting.schedule)
|
self.assertTrue(new_meeting.schedule)
|
||||||
|
self.assertEqual(new_meeting.schedule.name, 'secretary1')
|
||||||
|
self.assertTrue(new_meeting.schedule.base)
|
||||||
|
self.assertEqual(new_meeting.schedule.base.name, 'base')
|
||||||
self.assertEqual(new_meeting.attendees, None)
|
self.assertEqual(new_meeting.attendees, None)
|
||||||
|
|
||||||
def test_edit_meeting(self):
|
def test_edit_meeting(self):
|
||||||
|
@ -197,8 +200,7 @@ class SecrMeetingTestCase(TestCase):
|
||||||
|
|
||||||
# test delete
|
# test delete
|
||||||
# first unschedule sessions so we can delete
|
# first unschedule sessions so we can delete
|
||||||
SchedTimeSessAssignment.objects.filter(schedule=meeting.schedule).delete()
|
SchedTimeSessAssignment.objects.filter(schedule__in=[meeting.schedule, meeting.schedule.base, meeting.unofficial_schedule]).delete()
|
||||||
SchedTimeSessAssignment.objects.filter(schedule=meeting.unofficial_schedule).delete()
|
|
||||||
self.client.login(username="secretary", password="secretary+password")
|
self.client.login(username="secretary", password="secretary+password")
|
||||||
post_dict = {
|
post_dict = {
|
||||||
'room-TOTAL_FORMS': q('input[name="room-TOTAL_FORMS"]').val(),
|
'room-TOTAL_FORMS': q('input[name="room-TOTAL_FORMS"]').val(),
|
||||||
|
@ -339,27 +341,29 @@ class SecrMeetingTestCase(TestCase):
|
||||||
|
|
||||||
def test_meetings_misc_session_delete(self):
|
def test_meetings_misc_session_delete(self):
|
||||||
meeting = make_meeting_test_data()
|
meeting = make_meeting_test_data()
|
||||||
slot = meeting.schedule.assignments.filter(timeslot__type='reg').first().timeslot
|
schedule = meeting.schedule.base
|
||||||
url = reverse('ietf.secr.meetings.views.misc_session_delete', kwargs={'meeting_id':meeting.number,'schedule_name':meeting.schedule.name,'slot_id':slot.id})
|
slot = schedule.assignments.filter(timeslot__type='reg').first().timeslot
|
||||||
target = reverse('ietf.secr.meetings.views.misc_sessions', kwargs={'meeting_id':meeting.number,'schedule_name':meeting.schedule.name})
|
url = reverse('ietf.secr.meetings.views.misc_session_delete', kwargs={'meeting_id':meeting.number,'schedule_name':schedule.name,'slot_id':slot.id})
|
||||||
|
target = reverse('ietf.secr.meetings.views.misc_sessions', kwargs={'meeting_id':meeting.number,'schedule_name':schedule.name})
|
||||||
self.client.login(username="secretary", password="secretary+password")
|
self.client.login(username="secretary", password="secretary+password")
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
response = self.client.post(url, {'post':'yes'})
|
response = self.client.post(url, {'post':'yes'})
|
||||||
self.assertRedirects(response, target)
|
self.assertRedirects(response, target)
|
||||||
self.assertFalse(meeting.schedule.assignments.filter(timeslot=slot))
|
self.assertFalse(schedule.assignments.filter(timeslot=slot))
|
||||||
|
|
||||||
def test_meetings_misc_session_cancel(self):
|
def test_meetings_misc_session_cancel(self):
|
||||||
meeting = make_meeting_test_data()
|
meeting = make_meeting_test_data()
|
||||||
slot = meeting.schedule.assignments.filter(timeslot__type='reg').first().timeslot
|
schedule = meeting.schedule.base
|
||||||
url = reverse('ietf.secr.meetings.views.misc_session_cancel', kwargs={'meeting_id':meeting.number,'schedule_name':meeting.schedule.name,'slot_id':slot.id})
|
slot = schedule.assignments.filter(timeslot__type='reg').first().timeslot
|
||||||
redirect_url = reverse('ietf.secr.meetings.views.misc_sessions', kwargs={'meeting_id':meeting.number,'schedule_name':meeting.schedule.name})
|
url = reverse('ietf.secr.meetings.views.misc_session_cancel', kwargs={'meeting_id':meeting.number,'schedule_name':schedule.name,'slot_id':slot.id})
|
||||||
|
redirect_url = reverse('ietf.secr.meetings.views.misc_sessions', kwargs={'meeting_id':meeting.number,'schedule_name':schedule.name})
|
||||||
self.client.login(username="secretary", password="secretary+password")
|
self.client.login(username="secretary", password="secretary+password")
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
response = self.client.post(url, {'post':'yes'})
|
response = self.client.post(url, {'post':'yes'})
|
||||||
self.assertRedirects(response, redirect_url)
|
self.assertRedirects(response, redirect_url)
|
||||||
session = slot.sessionassignments.filter(schedule=meeting.schedule).first().session
|
session = slot.sessionassignments.filter(schedule=schedule).first().session
|
||||||
self.assertEqual(SchedulingEvent.objects.filter(session=session).order_by('-id')[0].status_id, 'canceled')
|
self.assertEqual(SchedulingEvent.objects.filter(session=session).order_by('-id')[0].status_id, 'canceled')
|
||||||
|
|
||||||
def test_meetings_regular_session_edit(self):
|
def test_meetings_regular_session_edit(self):
|
||||||
|
|
|
@ -22,7 +22,6 @@ from ietf.meeting.utils import add_event_info_to_session_qs
|
||||||
from ietf.meeting.utils import only_sessions_that_can_meet
|
from ietf.meeting.utils import only_sessions_that_can_meet
|
||||||
from ietf.name.models import SessionStatusName
|
from ietf.name.models import SessionStatusName
|
||||||
from ietf.group.models import Group, GroupEvent
|
from ietf.group.models import Group, GroupEvent
|
||||||
from ietf.person.models import Person
|
|
||||||
from ietf.secr.meetings.blue_sheets import create_blue_sheets
|
from ietf.secr.meetings.blue_sheets import create_blue_sheets
|
||||||
from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, MeetingModelForm, MeetingSelectForm,
|
from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, MeetingModelForm, MeetingSelectForm,
|
||||||
MeetingRoomForm, MiscSessionForm, TimeSlotForm, RegularSessionEditForm,
|
MeetingRoomForm, MiscSessionForm, TimeSlotForm, RegularSessionEditForm,
|
||||||
|
@ -85,21 +84,19 @@ def check_misc_sessions(meeting,schedule):
|
||||||
Ensure misc session timeslots exist and have appropriate SchedTimeSessAssignment objects
|
Ensure misc session timeslots exist and have appropriate SchedTimeSessAssignment objects
|
||||||
for the specified schedule.
|
for the specified schedule.
|
||||||
'''
|
'''
|
||||||
|
# FIXME: this is a legacy function: delete it once base schedules are rolled out
|
||||||
|
|
||||||
|
if Schedule.objects.filter(meeting=meeting, base__isnull=False).exists():
|
||||||
|
return
|
||||||
|
|
||||||
slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary','lead','offagenda'))
|
slots = TimeSlot.objects.filter(meeting=meeting,type__in=('break','reg','other','plenary','lead','offagenda'))
|
||||||
plenary = slots.filter(type='plenary').first()
|
plenary = slots.filter(type='plenary').first()
|
||||||
if plenary:
|
if plenary:
|
||||||
assignments = plenary.sessionassignments.all()
|
assignments = plenary.sessionassignments.all()
|
||||||
if not assignments.filter(schedule=schedule):
|
if not assignments.filter(schedule=schedule):
|
||||||
source = assignments.first().schedule
|
source = assignments.first().schedule
|
||||||
copy_assignments(slots,source,schedule)
|
for ss in SchedTimeSessAssignment.objects.filter(schedule=source,timeslot__in=slots):
|
||||||
|
SchedTimeSessAssignment.objects.create(schedule=schedule,session=ss.session,timeslot=ss.timeslot)
|
||||||
def copy_assignments(slots,source,target):
|
|
||||||
'''
|
|
||||||
Copy SchedTimeSessAssignment objects from source schedule to target schedule. Slots is
|
|
||||||
a queryset of slots
|
|
||||||
'''
|
|
||||||
for ss in SchedTimeSessAssignment.objects.filter(schedule=source,timeslot__in=slots):
|
|
||||||
SchedTimeSessAssignment.objects.create(schedule=target,session=ss.session,timeslot=ss.timeslot)
|
|
||||||
|
|
||||||
def get_last_meeting(meeting):
|
def get_last_meeting(meeting):
|
||||||
last_number = int(meeting.number) - 1
|
last_number = int(meeting.number) - 1
|
||||||
|
@ -221,13 +218,23 @@ def add(request):
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
meeting = form.save()
|
meeting = form.save()
|
||||||
|
|
||||||
|
base_schedule = Schedule.objects.create(
|
||||||
|
meeting=meeting,
|
||||||
|
name='base',
|
||||||
|
owner=request.user.person,
|
||||||
|
visible=True,
|
||||||
|
public=True
|
||||||
|
)
|
||||||
|
|
||||||
schedule = Schedule.objects.create(meeting = meeting,
|
schedule = Schedule.objects.create(meeting = meeting,
|
||||||
name = 'Empty-Schedule',
|
name = "{}1".format(request.user.username),
|
||||||
owner = Person.objects.get(name='(System)'),
|
owner = request.user.person,
|
||||||
visible = True,
|
visible = True,
|
||||||
public = True)
|
public = True,
|
||||||
|
base = base_schedule,
|
||||||
|
)
|
||||||
meeting.schedule = schedule
|
meeting.schedule = schedule
|
||||||
|
|
||||||
# we want to carry session request lock status over from previous meeting
|
# we want to carry session request lock status over from previous meeting
|
||||||
previous_meeting = get_meeting( int(meeting.number) - 1 )
|
previous_meeting = get_meeting( int(meeting.number) - 1 )
|
||||||
meeting.session_request_lock_message = previous_meeting.session_request_lock_message
|
meeting.session_request_lock_message = previous_meeting.session_request_lock_message
|
||||||
|
@ -296,7 +303,7 @@ def blue_sheet_generate(request, meeting_id):
|
||||||
# TODO: Why aren't 'ag' in here as well?
|
# TODO: Why aren't 'ag' in here as well?
|
||||||
groups = Group.objects.filter(
|
groups = Group.objects.filter(
|
||||||
type__in=['wg','rg'],
|
type__in=['wg','rg'],
|
||||||
session__timeslotassignments__schedule=meeting.schedule).order_by('acronym')
|
session__timeslotassignments__schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None]).order_by('acronym')
|
||||||
create_blue_sheets(meeting, groups)
|
create_blue_sheets(meeting, groups)
|
||||||
|
|
||||||
messages.success(request, 'Blue Sheets generated')
|
messages.success(request, 'Blue Sheets generated')
|
||||||
|
@ -380,7 +387,7 @@ def misc_sessions(request, meeting_id, schedule_name):
|
||||||
check_misc_sessions(meeting,schedule)
|
check_misc_sessions(meeting,schedule)
|
||||||
|
|
||||||
misc_session_types = ['break','reg','other','plenary','lead']
|
misc_session_types = ['break','reg','other','plenary','lead']
|
||||||
assignments = schedule.assignments.filter(timeslot__type__in=misc_session_types)
|
assignments = SchedTimeSessAssignment.objects.filter(schedule__in=[schedule, schedule.base], timeslot__type__in=misc_session_types)
|
||||||
assignments = assignments.order_by('-timeslot__type__name','timeslot__time')
|
assignments = assignments.order_by('-timeslot__type__name','timeslot__time')
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
@ -571,7 +578,7 @@ def notifications(request, meeting_id):
|
||||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||||
last_notice = GroupEvent.objects.filter(type='sent_notification').first()
|
last_notice = GroupEvent.objects.filter(type='sent_notification').first()
|
||||||
groups = set()
|
groups = set()
|
||||||
for ss in meeting.schedule.assignments.filter(timeslot__type='regular'):
|
for ss in SchedTimeSessAssignment.objects.filter(schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None], timeslot__type='regular'):
|
||||||
last_notice = ss.session.group.latest_event(type='sent_notification')
|
last_notice = ss.session.group.latest_event(type='sent_notification')
|
||||||
if last_notice and ss.modified > last_notice.time:
|
if last_notice and ss.modified > last_notice.time:
|
||||||
groups.add(ss.session.group)
|
groups.add(ss.session.group)
|
||||||
|
@ -652,7 +659,7 @@ def regular_sessions(request, meeting_id, schedule_name):
|
||||||
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
||||||
|
|
||||||
sessions = add_event_info_to_session_qs(
|
sessions = add_event_info_to_session_qs(
|
||||||
only_sessions_that_can_meet(schedule.meeting.session_set)
|
only_sessions_that_can_meet(meeting.session_set)
|
||||||
).order_by('group__acronym')
|
).order_by('group__acronym')
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
|
|
@ -32,11 +32,10 @@ VIDEO_TITLE_RE = re.compile(r'IETF(?P<number>[\d]+)-(?P<name>.*)-(?P<date>\d{8})
|
||||||
def _get_session(number,name,date,time):
|
def _get_session(number,name,date,time):
|
||||||
'''Lookup session using data from video title'''
|
'''Lookup session using data from video title'''
|
||||||
meeting = Meeting.objects.get(number=number)
|
meeting = Meeting.objects.get(number=number)
|
||||||
schedule = meeting.schedule
|
|
||||||
timeslot_time = datetime.datetime.strptime(date + time,'%Y%m%d%H%M')
|
timeslot_time = datetime.datetime.strptime(date + time,'%Y%m%d%H%M')
|
||||||
try:
|
try:
|
||||||
assignment = SchedTimeSessAssignment.objects.get(
|
assignment = SchedTimeSessAssignment.objects.get(
|
||||||
schedule = schedule,
|
schedule__in = [meeting.schedule, meeting.schedule.base],
|
||||||
session__group__acronym = name.lower(),
|
session__group__acronym = name.lower(),
|
||||||
timeslot__time = timeslot_time,
|
timeslot__time = timeslot_time,
|
||||||
)
|
)
|
||||||
|
@ -108,7 +107,7 @@ def get_timeslot_for_filename(filename):
|
||||||
meeting=meeting,
|
meeting=meeting,
|
||||||
location__name=room_mapping[match.groupdict()['room']],
|
location__name=room_mapping[match.groupdict()['room']],
|
||||||
time=time,
|
time=time,
|
||||||
sessionassignments__schedule=meeting.schedule,
|
sessionassignments__schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None],
|
||||||
).distinct()
|
).distinct()
|
||||||
uncancelled_slots = [t for t in slots if not add_event_info_to_session_qs(t.sessions.all()).filter(current_status='canceled').exists()]
|
uncancelled_slots = [t for t in slots if not add_event_info_to_session_qs(t.sessions.all()).filter(current_status='canceled').exists()]
|
||||||
return uncancelled_slots[0]
|
return uncancelled_slots[0]
|
||||||
|
|
|
@ -232,9 +232,12 @@ def recording(request, meeting_num):
|
||||||
session.
|
session.
|
||||||
'''
|
'''
|
||||||
meeting = get_object_or_404(Meeting, number=meeting_num)
|
meeting = get_object_or_404(Meeting, number=meeting_num)
|
||||||
assignments = meeting.schedule.assignments.exclude(session__type__in=('reg','break')).order_by('session__group__acronym')
|
sessions = Session.objects.filter(
|
||||||
sessions = [ x.session for x in assignments ]
|
timeslotassignments__schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None]
|
||||||
|
).exclude(
|
||||||
|
type__in=['reg','break']
|
||||||
|
).order_by('group__acronym')
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = RecordingForm(request.POST,meeting=meeting)
|
form = RecordingForm(request.POST,meeting=meeting)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
|
|
@ -461,6 +461,11 @@ input.draft-file-input {
|
||||||
Meeting Tool
|
Meeting Tool
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
|
|
||||||
|
#misc-sessions .from-base-schedule {
|
||||||
|
text-align: centeR;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
#misc-session-edit-form input[type="text"] {
|
#misc-session-edit-form input[type="text"] {
|
||||||
width: 30em;
|
width: 30em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,13 +33,17 @@
|
||||||
<td>{{ assignment.timeslot.location }}</td>
|
<td>{{ assignment.timeslot.location }}</td>
|
||||||
<td>{{ assignment.timeslot.show_location }}</td>
|
<td>{{ assignment.timeslot.show_location }}</td>
|
||||||
<td>{{ assignment.timeslot.type }}</td>
|
<td>{{ assignment.timeslot.type }}</td>
|
||||||
<td><a href="{% url "ietf.secr.meetings.views.misc_session_edit" meeting_id=meeting.number schedule_name=schedule.name slot_id=assignment.timeslot.id %}">Edit</a></td>
|
{% if assignment.schedule_id == schedule.pk %}
|
||||||
<td>
|
<td><a href="{% url "ietf.secr.meetings.views.misc_session_edit" meeting_id=meeting.number schedule_name=schedule.name slot_id=assignment.timeslot.id %}">Edit</a></td>
|
||||||
{% if not assignment.session.type.slug == "break" %}
|
<td>
|
||||||
<a href="{% url "ietf.secr.meetings.views.misc_session_cancel" meeting_id=meeting.number schedule_name=schedule.name slot_id=assignment.timeslot.id %}">Cancel</a>
|
{% if assignment.session.type.slug != "break" %}
|
||||||
{% endif %}
|
<a href="{% url "ietf.secr.meetings.views.misc_session_cancel" meeting_id=meeting.number schedule_name=schedule.name slot_id=assignment.timeslot.id %}">Cancel</a>
|
||||||
</td>
|
{% endif %}
|
||||||
<td><a href="{% url "ietf.secr.meetings.views.misc_session_delete" meeting_id=meeting.number schedule_name=schedule.name slot_id=assignment.timeslot.id %}">Delete</a></td>
|
</td>
|
||||||
|
<td><a href="{% url "ietf.secr.meetings.views.misc_session_delete" meeting_id=meeting.number schedule_name=schedule.name slot_id=assignment.timeslot.id %}">Delete</a></td>
|
||||||
|
{% else %}
|
||||||
|
<td colspan="3" class="from-base-schedule">(from base schedule)</td>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -52,7 +52,7 @@ def get_session(timeslot, schedule=None):
|
||||||
# todo, doesn't account for shared timeslot
|
# todo, doesn't account for shared timeslot
|
||||||
if not schedule:
|
if not schedule:
|
||||||
schedule = timeslot.meeting.schedule
|
schedule = timeslot.meeting.schedule
|
||||||
qs = timeslot.sessions.filter(timeslotassignments__schedule=schedule) #.exclude(states__slug='deleted')
|
qs = timeslot.sessions.filter(timeslotassignments__schedule__in=[schedule, schedule.base if schedule else None]) #.exclude(states__slug='deleted')
|
||||||
if qs:
|
if qs:
|
||||||
return qs[0]
|
return qs[0]
|
||||||
else:
|
else:
|
||||||
|
@ -66,7 +66,7 @@ def get_timeslot(session, schedule=None):
|
||||||
'''
|
'''
|
||||||
if not schedule:
|
if not schedule:
|
||||||
schedule = session.meeting.schedule
|
schedule = session.meeting.schedule
|
||||||
ss = session.timeslotassignments.filter(schedule=schedule)
|
ss = session.timeslotassignments.filter(schedule__in=[schedule, schedule.base if schedule else None])
|
||||||
if ss:
|
if ss:
|
||||||
return ss[0].timeslot
|
return ss[0].timeslot
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1010,6 +1010,9 @@ a.fc-event, .fc-event, .fc-content, .fc-title, .fc-event-container {
|
||||||
margin-left: 0.2em;
|
margin-left: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.from-base-schedule {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
/* === Edit Meeting Schedule ====================================== */
|
/* === Edit Meeting Schedule ====================================== */
|
||||||
|
|
||||||
|
@ -1138,7 +1141,7 @@ a.fc-event, .fc-event, .fc-content, .fc-title, .fc-event-container {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-meeting-schedule .session.tombstone {
|
.edit-meeting-schedule .session.readonly {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ jQuery(document).ready(function () {
|
||||||
alert("Error: " + errorText);
|
alert("Error: " + errorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sessions = content.find(".session").not(".tombstone");
|
let sessions = content.find(".session").not(".readonly");
|
||||||
let timeslots = content.find(".timeslot");
|
let timeslots = content.find(".timeslot");
|
||||||
let days = content.find(".day-flow .day");
|
let days = content.find(".day-flow .day");
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ ul.sessionlist { list-style:none; padding-left:2em; margin-bottom:10px;}
|
||||||
<li class="roomlistentry"><h3>{{room.grouper|default:"Location Unavailable"}}</h3>
|
<li class="roomlistentry"><h3>{{room.grouper|default:"Location Unavailable"}}</h3>
|
||||||
<ul class="sessionlist">
|
<ul class="sessionlist">
|
||||||
{% for ss in room.list %}
|
{% for ss in room.list %}
|
||||||
<li class="sessionlistentry type-{{ss.timeslot.type.slug}}">{{ss.timeslot.time|date:"H:i"}}-{{ss.timeslot.end_time|date:"H:i"}} {{ss.session.short_name}}</li>
|
<li class="sessionlistentry type-{{ss.timeslot.type_id}} {% if ss.schedule_id != meeting.schedule_id %}from-base-schedule{% endif %}">{{ss.timeslot.time|date:"H:i"}}-{{ss.timeslot.end_time|date:"H:i"}} {{ss.session.short_name}}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -29,7 +29,7 @@ li.daylistentry { margin-left:2em; font-weight: 400; }
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include "meeting/meeting_heading.html" with updated=meeting.updated selected="by-type" title_extra="by Session Type" %}
|
{% include "meeting/meeting_heading.html" with updated=meeting.updated selected="by-type" title_extra="by Session Type" %}
|
||||||
|
|
||||||
{% regroup assignments by session.type.slug as type_list %}
|
{% regroup assignments by session.type_id as type_list %}
|
||||||
<ul class="typelist">
|
<ul class="typelist">
|
||||||
{% for type in type_list %}
|
{% for type in type_list %}
|
||||||
<li class="typelistentry {% cycle 'even' 'odd' %}">
|
<li class="typelistentry {% cycle 'even' 'odd' %}">
|
||||||
|
@ -41,11 +41,11 @@ li.daylistentry { margin-left:2em; font-weight: 400; }
|
||||||
<h3>{{ day.grouper }}</h3>
|
<h3>{{ day.grouper }}</h3>
|
||||||
<table class="sessiontable">
|
<table class="sessiontable">
|
||||||
{% for ss in day.list %}
|
{% for ss in day.list %}
|
||||||
<tr>
|
<tr {% if ss.schedule_id != meeting.schedule_id %}class="from-base-schedule"{% endif %}>
|
||||||
<td>{{ss.timeslot.time|date:"H:i"}}-{{ss.timeslot.end_time|date:"H:i"}}</td>
|
<td>{{ss.timeslot.time|date:"H:i"}}-{{ss.timeslot.end_time|date:"H:i"}}</td>
|
||||||
<td>{{ss.timeslot.get_hidden_location}}</td>
|
<td>{{ss.timeslot.get_hidden_location}}</td>
|
||||||
<td class="type-{{ss.session.type.slug}}">{{ss.session.short_name}} </td>
|
<td class="type-{{ss.session.type_id}}">{{ss.session.short_name}} </td>
|
||||||
<td>{% if ss.session.type_id == 'regular' or ss.session.type_id == 'plenary' or ss.session.type_id == 'other' %} <a href="{% url 'ietf.meeting.views.session_details' num=ss.session.meeting.number acronym=ss.session.group.acronym %}">Materials</a>{% else %} {% endif %}</td>
|
<td>{% if ss.session.type_id == 'regular' or ss.session.type_id == 'plenary' or ss.session.type_id == 'other' %} <a href="{% url 'ietf.meeting.views.session_details' num=meeting.number acronym=ss.session.group.acronym %}">Materials</a>{% else %} {% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
·
|
·
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<a href="{% url "ietf.meeting.views.copy_meeting_schedule" num=meeting.number owner=schedule.owner_email name=schedule.name %}">Copy agenda</a>
|
<a href="{% url "ietf.meeting.views.new_meeting_schedule" num=meeting.number owner=schedule.owner_email name=schedule.name %}">New agenda</a>
|
||||||
·
|
·
|
||||||
|
|
||||||
<a href="{% url "ietf.meeting.views.list_schedules" num=meeting.number %}">Other Agendas</a>
|
<a href="{% url "ietf.meeting.views.list_schedules" num=meeting.number %}">Other Agendas</a>
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
{% if not can_edit %}
|
{% if not can_edit %}
|
||||||
·
|
·
|
||||||
|
|
||||||
<strong><em>You can't edit this schedule. Take a copy first.</em></strong>
|
<strong><em>You can't edit this schedule. Make a <a href="{% url "ietf.meeting.views.new_meeting_schedule" num=meeting.number owner=schedule.owner_email name=schedule.name %}">new agenda from this</a>.</em></strong>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div id="session{{ session.pk }}" class="session {% if not session.group.parent.scheduling_color %}untoggleable{% endif %} {% if session.parent_acronym %}parent-{{ session.parent_acronym }}{% endif %} {% if session.is_tombstone %}tombstone{% endif %}" style="width:{{ session.layout_width }}em;" data-duration="{{ session.requested_duration.total_seconds }}" {% if session.attendees != None %}data-attendees="{{ session.attendees }}"{% endif %}>
|
<div id="session{{ session.pk }}" class="session {% if not session.group.parent.scheduling_color %}untoggleable{% endif %} {% if session.parent_acronym %}parent-{{ session.parent_acronym }}{% endif %} {% if session.readonly %}readonly{% endif %}" style="width:{{ session.layout_width }}em;" data-duration="{{ session.requested_duration.total_seconds }}" {% if session.attendees != None %}data-attendees="{{ session.attendees }}"{% endif %}>
|
||||||
<div class="session-label {% if session.group and session.group.is_bof %}bof-session{% endif %}">
|
<div class="session-label {% if session.group and session.group.is_bof %}bof-session{% endif %}">
|
||||||
{{ session.scheduling_label }}
|
{{ session.scheduling_label }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
{% load ietf_filters %}{% if is_change %}MEETING DETAILS HAVE CHANGED. SEE LATEST DETAILS BELOW.
|
{% load ietf_filters %}{% if is_change %}MEETING DETAILS HAVE CHANGED. SEE LATEST DETAILS BELOW.
|
||||||
|
|
||||||
{% endif %}The {{ group.name }} ({{ group.acronym }}) {% if group.type.slug == 'wg' and group.state.slug == 'bof' %}BOF{% else %}{{group.type.name}}{% endif %} will hold
|
{% endif %}The {{ group.name }} ({{ group.acronym }}) {% if group.type.slug == 'wg' and group.state.slug == 'bof' %}BOF{% else %}{{group.type.name}}{% endif %} will hold
|
||||||
{% if meeting.session_set.count == 1 %}a{% if meeting.city %}n {% else %} virtual {% endif %}interim meeting on {{ meeting.date }} from {{ meeting.schedule.assignments.first.timeslot.time | date:"H:i" }} to {{ meeting.schedule.assignments.first.timeslot.end_time | date:"H:i" }} {{ meeting.time_zone}}{% if meeting.time_zone != 'UTC' %} ({{ meeting.schedule.assignments.first.timeslot.utc_start_time | date:"H:i" }} to {{ meeting.schedule.assignments.first.timeslot.utc_end_time | date:"H:i" }} UTC){% endif %}.
|
{% if meeting.session_set.count == 1 %}a{% if meeting.city %}n {% else %} virtual {% endif %}interim meeting on {{ meeting.date }} from {{ assignments.first.timeslot.time | date:"H:i" }} to {{ assignments.first.timeslot.end_time | date:"H:i" }} {{ meeting.time_zone}}{% if meeting.time_zone != 'UTC' %} ({{ assignments.first.timeslot.utc_start_time | date:"H:i" }} to {{ assignments.first.timeslot.utc_end_time | date:"H:i" }} UTC){% endif %}.
|
||||||
{% else %}a multi-day {% if not meeting.city %}virtual {% endif %}interim meeting.
|
{% else %}a multi-day {% if not meeting.city %}virtual {% endif %}interim meeting.
|
||||||
|
|
||||||
{% for assignment in meeting.schedule.assignments.all %}Session {{ forloop.counter }}:
|
{% for assignment in assignments %}Session {{ forloop.counter }}:
|
||||||
{{ assignment.timeslot.time | date:"Y-m-d" }} {{ assignment.timeslot.time | date:"H:i" }} to {{ assignment.timeslot.end_time | date:"H:i" }} {{ meeting.time_zone }}{% if meeting.time_zone != 'UTC' %}({{ assignment.timeslot.utc_start_time | date:"H:i" }} to {{ assignment.timeslot.utc_end_time | date:"H:i" }} UTC){% endif %}
|
{{ assignment.timeslot.time | date:"Y-m-d" }} {{ assignment.timeslot.time | date:"H:i" }} to {{ assignment.timeslot.end_time | date:"H:i" }} {{ meeting.time_zone }}{% if meeting.time_zone != 'UTC' %}({{ assignment.timeslot.utc_start_time | date:"H:i" }} to {{ assignment.timeslot.utc_end_time | date:"H:i" }} UTC){% endif %}
|
||||||
{% endfor %}{% endif %}
|
{% endfor %}{% endif %}
|
||||||
{% if meeting.city %}Meeting Location:
|
{% if meeting.city %}Meeting Location:
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<dd>{{ meeting.country }}</dd>
|
<dd>{{ meeting.country }}</dd>
|
||||||
<dt>Timezone</dt>
|
<dt>Timezone</dt>
|
||||||
<dd>{{ meeting.time_zone }}</dd>
|
<dd>{{ meeting.time_zone }}</dd>
|
||||||
{% for assignment in meeting.schedule.assignments.all %}
|
{% for assignment in assignments %}
|
||||||
<br>
|
<br>
|
||||||
<dt>Date</dt>
|
<dt>Date</dt>
|
||||||
<dd>{{ assignment.timeslot.time|date:"Y-m-d" }}
|
<dd>{{ assignment.timeslot.time|date:"Y-m-d" }}
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% origin %}
|
{% origin %}
|
||||||
<h1>{% block title %}Copy agenda {{ schedule.name }}{% endblock %}</h1>
|
<h1>{% block title %}{% if schedule %}Copy agenda {{ schedule.name }} to new agenda{% else %}New agenda{% endif %}{% endblock %}</h1>
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% bootstrap_form form %}
|
{% bootstrap_form form %}
|
||||||
|
|
||||||
{% buttons %}
|
{% buttons %}
|
||||||
<button type="submit" class="btn btn-default">Copy agenda</button>
|
<button type="submit" class="btn btn-primary">Create agenda</button>
|
||||||
{% endbuttons %}
|
{% endbuttons %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -8,13 +8,13 @@
|
||||||
<script src="{% static 'ietf/bootstrap/js/bootstrap.min.js' %}"></script>
|
<script src="{% static 'ietf/bootstrap/js/bootstrap.min.js' %}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
{% autoescape off %}
|
||||||
var room_names = [{% for room in rooms %}"{{room.name}}"{% if not forloop.last %},{% endif %}{% endfor %}];
|
var room_names = [{% for room in rooms %}"{{room.name}}"{% if not forloop.last %},{% endif %}{% endfor %}];
|
||||||
var room_functional_names = [{% for room in rooms %}"{{room.functional_name}}"{% if not forloop.last %},{% endif %}{% endfor %}];
|
var room_functional_names = [{% for room in rooms %}"{{room.functional_name}}"{% if not forloop.last %},{% endif %}{% endfor %}];
|
||||||
var room_typelabels = [{% for room in rooms %}"{% for type in room.session_types.all %}{{type.name}}{% if not forloop.last %}, {% endif %}{% endfor %}"{% if not forloop.last %},{% endif %}{% endfor %}];
|
var room_typelabels = [{% for room in rooms %}"{% for type in room.session_types.all %}{{type.name}}{% if not forloop.last %}, {% endif %}{% endfor %}"{% if not forloop.last %},{% endif %}{% endfor %}];
|
||||||
|
|
||||||
var items = new Array();
|
var items = new Array();
|
||||||
|
|
||||||
{% autoescape off %}
|
|
||||||
{% for slot in unavailable %}
|
{% for slot in unavailable %}
|
||||||
if (room_names.indexOf("{{slot.get_hidden_location}}") >= 0 )
|
if (room_names.indexOf("{{slot.get_hidden_location}}") >= 0 )
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
{% for ss in assignments %}
|
{% for ss in assignments %}
|
||||||
if (room_names.indexOf("{{ss.timeslot.get_hidden_location}}") >= 0 )
|
if (room_names.indexOf("{{ss.timeslot.get_hidden_location}}") >= 0 )
|
||||||
{
|
{
|
||||||
items.push({room_index:room_names.indexOf("{{ss.timeslot.get_hidden_location}}"),day:{{ss.day}}, delta_from_beginning:{{ss.delta_from_beginning}},time:"{{ss.timeslot.time|date:"Hi"}}-{{ss.timeslot.end_time|date:"Hi"}}", verbose_time:"{{ss.timeslot.time|date:"D M d Hi"}}-{{ss.timeslot.end_time|date:"Hi"}}",duration:{{ss.timeslot.duration.total_seconds}}, type:"{{ss.timeslot.type}}", {% if ss.session.name %}name:"{{ss.session.name|escapejs}}",{% if ss.session.group.acronym %} wg:"{{ss.session.group.acronym}}",{%endif%}{% else %}{% if ss.timeslot.type.name == "Break" %}name:"{{ss.timeslot.name|escapejs}}", area:"break", wg:"break",{% elif ss.timeslot.type.slug == "unavail" %}name:"Unavailable",{% else %}name:"{{ss.session.group.name|escapejs}}{%if ss.session.group.state.name == "BOF"%} BOF{%endif%}",wg:"{{ss.session.group.acronym}}",state:"{{ss.session.group.state}}",area:"{{ss.session.group.parent.acronym}}",{% endif %}{% endif %} dayname:"{{ ss.timeslot.time|date:"l"|upper }}, {{ ss.timeslot.time|date:"F j, Y" }}"{% if ss.session.agenda %}, agenda:"{{ss.session.agenda.get_href}}"{% endif %} });
|
items.push({room_index:room_names.indexOf("{{ss.timeslot.get_hidden_location}}"),day:{{ss.day}}, delta_from_beginning:{{ss.delta_from_beginning}},time:"{{ss.timeslot.time|date:"Hi"}}-{{ss.timeslot.end_time|date:"Hi"}}", verbose_time:"{{ss.timeslot.time|date:"D M d Hi"}}-{{ss.timeslot.end_time|date:"Hi"}}",duration:{{ss.timeslot.duration.total_seconds}}, type:"{{ss.timeslot.type}}", {% if ss.session.name %}name:"{{ss.session.name|escapejs}}",{% if ss.session.group.acronym %} wg:"{{ss.session.group.acronym}}",{%endif%}{% else %}{% if ss.timeslot.type.name == "Break" %}name:"{{ss.timeslot.name|escapejs}}", area:"break", wg:"break",{% elif ss.timeslot.type_id == "unavail" %}name:"Unavailable",{% else %}name:"{{ss.session.group.name|escapejs}}{%if ss.session.group.state.name == "BOF"%} BOF{%endif%}",wg:"{{ss.session.group.acronym}}",state:"{{ss.session.group.state}}",area:"{{ss.session.group.parent.acronym}}",{% endif %}{% endif %} dayname:"{{ ss.timeslot.time|date:"l"|upper }}, {{ ss.timeslot.time|date:"F j, Y" }}"{% if ss.session.agenda %}, agenda:"{{ss.session.agenda.get_href}}"{% endif %}, from_base_schedule: {% if ss.schedule_id != meeting.schedule_id %}true{% else %}false{% endif %} });
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
@ -353,6 +353,9 @@
|
||||||
e.style.padding=padding;
|
e.style.padding=padding;
|
||||||
e.style.fontFamily="sans-serif";
|
e.style.fontFamily="sans-serif";
|
||||||
e.style.fontSize="8pt";
|
e.style.fontSize="8pt";
|
||||||
|
if (items[i].from_base_schedule)
|
||||||
|
e.style.opacity = 0.5;
|
||||||
|
|
||||||
e.id=i;
|
e.id=i;
|
||||||
|
|
||||||
e.onmouseover=resize_func(e,sess_top,room_left,
|
e.onmouseover=resize_func(e,sess_top,room_left,
|
||||||
|
|
|
@ -5,25 +5,34 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% origin %}
|
{% origin %}
|
||||||
|
|
||||||
<h1>{% block title %}Possible Meeting Agendas for IETF {{ meeting.number }}{% endblock %}</h1>
|
<h1>{% block title %}Possible Meeting Agendas for IETF {{ meeting.number }}{% endblock %}</h1>
|
||||||
|
|
||||||
{% comment %}
|
{% comment %}
|
||||||
<div>
|
<div>
|
||||||
<p><a href="{% url "ietf.meeting.views.edit_timeslots" meeting.number %}">Edit Timeslots</a></p>
|
<p><a href="{% url "ietf.meeting.views.edit_timeslots" meeting.number %}">Edit Timeslots</a></p>
|
||||||
</div>
|
</div>
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{% for label, schedules in schedule_groups %}
|
{% for schedules, own, label in schedule_groups %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">{{ label }}</div>
|
<div class="panel-heading">
|
||||||
|
{{ label }}
|
||||||
|
{% if own %}
|
||||||
|
<div class="pull-right" >
|
||||||
|
<a href="{% url "ietf.meeting.views.new_meeting_schedule" num=meeting.number %}"><i class="fa fa-plus"></i> New Agenda</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<table class="table table-condensed table-striped">
|
<table class="table table-condensed table-striped">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-md-2">Name</th>
|
<th class="col-md-2">Name</th>
|
||||||
<th class="col-md-2">Owner</th>
|
<th class="col-md-2">Owner</th>
|
||||||
<th class="col-md-1">Origin</th>
|
<th class="col-md-1">Origin</th>
|
||||||
<th class="col-md-4">Notes</th>
|
<th class="col-md-1">Base</th>
|
||||||
|
<th class="col-md-3">Notes</th>
|
||||||
<th class="col-md-1">Visible</th>
|
<th class="col-md-1">Visible</th>
|
||||||
<th class="col-md-1">Public</th>
|
<th class="col-md-1">Public</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -40,14 +49,19 @@
|
||||||
<td>{{ schedule.owner }}</td>
|
<td>{{ schedule.owner }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if schedule.origin %}
|
{% if schedule.origin %}
|
||||||
<a href="{% url "ietf.meeting.views.edit_schedule" meeting.number schedule.origin.owner_email schedule.name %}">{{ schedule.origin.name }}</a>
|
<a href="{% url "ietf.meeting.views.edit_schedule" meeting.number schedule.origin.owner_email schedule.origin.name %}">{{ schedule.origin.name }}</a>
|
||||||
<a href="{% url "ietf.meeting.views.diff_schedules" meeting.number %}?from_schedule={{ schedule.origin.name|urlencode }}&to_schedule={{ schedule.name|urlencode }}" title="{{ schedule.changes_from_origin }} change{{ schedule.changes_from_origin|pluralize }} from {{ schedule.origin.name }}">+{{ schedule.changes_from_origin }}</a>
|
<a href="{% url "ietf.meeting.views.diff_schedules" meeting.number %}?from_schedule={{ schedule.origin.name|urlencode }}&to_schedule={{ schedule.name|urlencode }}" title="{{ schedule.changes_from_origin }} change{{ schedule.changes_from_origin|pluralize }} from {{ schedule.origin.name }}">+{{ schedule.changes_from_origin }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ schedule.notes|linebreaksbr }}</td>
|
<td>
|
||||||
<td>
|
{% if schedule.base %}
|
||||||
{% if schedule.visible %}
|
<a href="{% url "ietf.meeting.views.edit_schedule" meeting.number schedule.base.owner_email schedule.base.name %}">{{ schedule.base.name }}</a>
|
||||||
<div class="label label-success">visible</div>
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{{ schedule.notes|linebreaksbr }}</td>
|
||||||
|
<td>
|
||||||
|
{% if schedule.visible %}
|
||||||
|
<div class="label label-success">visible</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="label label-danger">hidden</div>
|
<div class="label label-danger">hidden</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
Loading…
Reference in a new issue