Skip to content

Commit 82ad040

Browse files
Add 'Select Sessions" tab to agenda pages. Commit ready for merge.
- Legacy-Id: 19183
1 parent 044293b commit 82ad040

14 files changed

Lines changed: 909 additions & 127 deletions

ietf/meeting/helpers.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,16 @@ def get_schedule_by_name(meeting, owner, name):
166166
return meeting.schedule_set.filter(name = name).first()
167167

168168
def preprocess_assignments_for_agenda(assignments_queryset, meeting, extra_prefetches=()):
169+
"""Add computed properties to assignments
170+
171+
For each assignment a, adds
172+
a.start_timestamp
173+
a.end_timestamp
174+
a.session.historic_group
175+
a.session.historic_parent
176+
a.session.rescheduled_to (if rescheduled)
177+
a.session.prefetched_active_materials
178+
"""
169179
assignments_queryset = assignments_queryset.prefetch_related(
170180
'timeslot', 'timeslot__type', 'timeslot__meeting',
171181
'timeslot__location', 'timeslot__location__floorplan', 'timeslot__location__urlresource_set',
@@ -260,9 +270,9 @@ def filter_keywords_for_session(session):
260270
if group.state_id == 'bof':
261271
keywords.add('bof')
262272
keywords.add(group.acronym.lower())
263-
token = session.docname_token_only_for_multiple()
264-
if token is not None:
265-
keywords.add(group.acronym.lower() + "-" + token)
273+
specific_kw = filter_keyword_for_specific_session(session)
274+
if specific_kw is not None:
275+
keywords.add(specific_kw)
266276
area = getattr(group, 'historic_parent', group.parent)
267277

268278
# Only sessions belonging to "regular" groups should respond to the
@@ -276,6 +286,18 @@ def filter_keywords_for_session(session):
276286
keywords.update(['officehours', session.name.lower().replace(' ', '')])
277287
return sorted(list(keywords))
278288

289+
def filter_keyword_for_specific_session(session):
290+
"""Get keyword that identifies a specific session
291+
292+
Returns None if the session cannot be selected individually.
293+
"""
294+
group = getattr(session, 'historic_group', session.group)
295+
if group is None:
296+
return None
297+
kw = group.acronym.lower() # start with this
298+
token = session.docname_token_only_for_multiple()
299+
return kw if token is None else '{}-{}'.format(kw, token)
300+
279301
def read_session_file(type, num, doc):
280302
# XXXX FIXME: the path fragment in the code below should be moved to
281303
# settings.py. The *_PATH settings should be generalized to format()

ietf/meeting/templatetags/agenda_custom_tags.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44

55
from django import template
6+
from django.urls import reverse
67

78
register = template.Library()
89

@@ -60,3 +61,10 @@ def args(obj, arg):
6061
obj.__callArg += [arg]
6162
return obj
6263

64+
@register.simple_tag(name='webcal_url', takes_context=True)
65+
def webcal_url(context, viewname, *args, **kwargs):
66+
"""webcal URL for a view"""
67+
return 'webcal://{}{}'.format(
68+
context.request.get_host(),
69+
reverse(viewname, args=args, kwargs=kwargs)
70+
)

ietf/meeting/tests_js.py

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,7 +1355,65 @@ def __call__(self, driver):
13551355
wait.until(in_iframe_href('tz=america/halifax', 'weekview'))
13561356
except:
13571357
self.fail('iframe href not updated to contain selected time zone')
1358-
1358+
1359+
def test_agenda_session_selection(self):
1360+
wait = WebDriverWait(self.driver, 2)
1361+
url = self.absreverse('ietf.meeting.views.agenda_personalize', kwargs={'num': self.meeting.number})
1362+
self.driver.get(url)
1363+
1364+
# Verify that elements are all updated when the filters change. That the correct elements
1365+
# have the appropriate classes is a separate test.
1366+
elements_to_check = self.driver.find_elements_by_css_selector('.agenda-link.filterable')
1367+
self.assertGreater(len(elements_to_check), 0, 'No elements with agenda links to update were found')
1368+
1369+
self.assertFalse(
1370+
any(checkbox.is_selected()
1371+
for checkbox in self.driver.find_elements_by_css_selector(
1372+
'input.checkbox[name="selected-sessions"]')),
1373+
'Sessions were selected before being clicked',
1374+
)
1375+
1376+
mars_checkbox = self.driver.find_element_by_css_selector('input[type="checkbox"][name="selected-sessions"][data-filter-item="mars"]')
1377+
break_checkbox = self.driver.find_element_by_css_selector('input[type="checkbox"][name="selected-sessions"][data-filter-item="secretariat-sessb"]')
1378+
registration_checkbox = self.driver.find_element_by_css_selector('input[type="checkbox"][name="selected-sessions"][data-filter-item="secretariat-sessa"]')
1379+
secretariat_button = self.driver.find_element_by_css_selector('button[data-filter-item="secretariat"]')
1380+
1381+
mars_checkbox.click() # select mars session
1382+
try:
1383+
wait.until(
1384+
lambda driver: all('?show=mars' in el.get_attribute('href') for el in elements_to_check)
1385+
)
1386+
except TimeoutException:
1387+
self.fail('Some agenda links were not updated when mars session was selected')
1388+
self.assertTrue(mars_checkbox.is_selected(), 'mars session checkbox was not selected after being clicked')
1389+
self.assertFalse(break_checkbox.is_selected(), 'break checkbox was selected without being clicked')
1390+
self.assertFalse(registration_checkbox.is_selected(), 'registration checkbox was selected without being clicked')
1391+
1392+
mars_checkbox.click() # deselect mars session
1393+
try:
1394+
wait.until(
1395+
lambda driver: not any('?show=mars' in el.get_attribute('href') for el in elements_to_check)
1396+
)
1397+
except TimeoutException:
1398+
self.fail('Some agenda links were not updated when mars session was de-selected')
1399+
self.assertFalse(mars_checkbox.is_selected(), 'mars session checkbox was still selected after being clicked')
1400+
self.assertFalse(break_checkbox.is_selected(), 'break checkbox was selected without being clicked')
1401+
self.assertFalse(registration_checkbox.is_selected(), 'registration checkbox was selected without being clicked')
1402+
1403+
secretariat_button.click() # turn on all secretariat sessions
1404+
break_checkbox.click() # also select the break
1405+
1406+
try:
1407+
wait.until(
1408+
lambda driver: all(
1409+
'?show=secretariat&hide=secretariat-sessb' in el.get_attribute('href')
1410+
for el in elements_to_check
1411+
))
1412+
except TimeoutException:
1413+
self.fail('Some agenda links were not updated when secretariat group but not break was selected')
1414+
self.assertFalse(mars_checkbox.is_selected(), 'mars session checkbox was unexpectedly selected')
1415+
self.assertFalse(break_checkbox.is_selected(), 'break checkbox was unexpectedly selected')
1416+
self.assertTrue(registration_checkbox.is_selected(), 'registration checkbox was expected to be selected')
13591417

13601418
@ifSeleniumEnabled
13611419
class WeekviewTests(IetfSeleniumTestCase):
@@ -1693,7 +1751,10 @@ def do_upcoming_view_filter_test(self, querystring, visible_meetings=()):
16931751
self.assert_upcoming_view_filter_matches_ics_filter(querystring)
16941752

16951753
# Check the ical links
1696-
simplified_querystring = querystring.replace(' ', '%20') # encode spaces'
1754+
simplified_querystring = querystring.replace(' ', '') # remove spaces
1755+
if simplified_querystring in ['?show=', '?hide=', '?show=&hide=']:
1756+
simplified_querystring = '' # these empty querystrings will be dropped (not an exhaustive list)
1757+
16971758
ics_link = self.driver.find_element_by_link_text('Download as .ics')
16981759
self.assertIn(simplified_querystring, ics_link.get_attribute('href'))
16991760
webcal_link = self.driver.find_element_by_link_text('Subscribe with webcal')

ietf/meeting/tests_views.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from ietf.meeting.helpers import send_interim_approval_request
3636
from ietf.meeting.helpers import send_interim_meeting_cancellation_notice, send_interim_session_cancellation_notice
3737
from ietf.meeting.helpers import send_interim_minutes_reminder, populate_important_dates, update_important_dates
38+
from ietf.meeting.helpers import filter_keyword_for_specific_session
3839
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation, SlideSubmission, SchedulingEvent, Room, Constraint, ConstraintName
3940
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting, make_interim_test_data
4041
from ietf.meeting.utils import finalize, condition_slide_order
@@ -375,6 +376,63 @@ def test_agenda_week_view(self):
375376
self.assertEqual(r_with_tz.status_code,200)
376377
self.assertEqual(r.content, r_with_tz.content)
377378

379+
def test_agenda_personalize(self):
380+
"""Session selection page should have a checkbox for each session with appropriate keywords"""
381+
meeting = make_meeting_test_data()
382+
url = urlreverse("ietf.meeting.views.agenda_personalize",kwargs=dict(num=meeting.number))
383+
r = self.client.get(url)
384+
self.assertEqual(r.status_code,200)
385+
q = PyQuery(r.content)
386+
for assignment in SchedTimeSessAssignment.objects.filter(
387+
schedule__in=[meeting.schedule, meeting.schedule.base],
388+
timeslot__type__private=False,
389+
):
390+
row = q('#row-{}'.format(assignment.slug()))
391+
self.assertIsNotNone(row, 'No row for assignment {}'.format(assignment))
392+
checkboxes = row('input[type="checkbox"][name="selected-sessions"]')
393+
self.assertEqual(len(checkboxes), 1,
394+
'Row for assignment {} does not have a checkbox input'.format(assignment))
395+
checkbox = checkboxes.eq(0)
396+
self.assertEqual(
397+
checkbox.attr('data-filter-item'),
398+
filter_keyword_for_specific_session(assignment.session),
399+
)
400+
401+
def test_agenda_personalize_updates_urls(self):
402+
"""The correct URLs should be updated when filter settings change on the personalize agenda view
403+
404+
Tests that the expected elements have the necessary classes. The actual update of these fields
405+
is tested in the JS tests
406+
"""
407+
meeting = make_meeting_test_data()
408+
url = urlreverse("ietf.meeting.views.agenda_personalize",kwargs=dict(num=meeting.number))
409+
r = self.client.get(url)
410+
self.assertEqual(r.status_code,200)
411+
q = PyQuery(r.content)
412+
413+
# Find all the elements expected to be updated
414+
expected_elements = []
415+
nav_tab_anchors = q('ul.nav.nav-tabs > li > a')
416+
for anchor in nav_tab_anchors.items():
417+
text = anchor.text().strip()
418+
if text in ['Agenda', 'UTC Agenda', 'Select Sessions']:
419+
expected_elements.append(anchor)
420+
for btn in q('.buttonlist a.btn').items():
421+
text = btn.text().strip()
422+
if text in ['View customized agenda', 'Download as .ics', 'Subscribe with webcal']:
423+
expected_elements.append(btn)
424+
425+
# Check that all the expected elements have the correct classes
426+
for elt in expected_elements:
427+
self.assertTrue(elt.has_class('agenda-link'))
428+
self.assertTrue(elt.has_class('filterable'))
429+
430+
# Finally, check that there are no unexpected elements marked to be updated.
431+
# If there are, they should be added to the test above.
432+
self.assertEqual(len(expected_elements),
433+
len(q('.agenda-link.filterable')),
434+
'Unexpected elements updated')
435+
378436
@override_settings(MEETING_MATERIALS_SERVE_LOCALLY=False, MEETING_DOC_HREFS = settings.MEETING_DOC_CDN_HREFS)
379437
def test_materials_through_cdn(self):
380438
meeting = make_meeting_test_data(create_interims=True)

ietf/meeting/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
url(r'^agenda/by-type$', views.agenda_by_type),
4848
url(r'^agenda/by-type/(?P<type>[a-z]+)$', views.agenda_by_type),
4949
url(r'^agenda/by-type/(?P<type>[a-z]+)/ics$', views.agenda_by_type_ics),
50+
url(r'^agenda/personalize', views.agenda_personalize),
5051
url(r'^agendas/list$', views.list_schedules),
5152
url(r'^agendas/edit$', RedirectView.as_view(pattern_name='ietf.meeting.views.list_schedules', permanent=True)),
5253
url(r'^agendas/diff/$', views.diff_schedules),

0 commit comments

Comments
 (0)