Skip to content

Commit

Permalink
Add 'Select Sessions" tab to agenda pages. Commit ready for merge.
Browse files Browse the repository at this point in the history
 - Legacy-Id: 19183
  • Loading branch information
jennifer-richards committed Jul 1, 2021
1 parent 044293b commit 82ad040
Show file tree
Hide file tree
Showing 14 changed files with 909 additions and 127 deletions.
28 changes: 25 additions & 3 deletions ietf/meeting/helpers.py
Expand Up @@ -166,6 +166,16 @@ def get_schedule_by_name(meeting, owner, name):
return meeting.schedule_set.filter(name = name).first()

def preprocess_assignments_for_agenda(assignments_queryset, meeting, extra_prefetches=()):
"""Add computed properties to assignments
For each assignment a, adds
a.start_timestamp
a.end_timestamp
a.session.historic_group
a.session.historic_parent
a.session.rescheduled_to (if rescheduled)
a.session.prefetched_active_materials
"""
assignments_queryset = assignments_queryset.prefetch_related(
'timeslot', 'timeslot__type', 'timeslot__meeting',
'timeslot__location', 'timeslot__location__floorplan', 'timeslot__location__urlresource_set',
Expand Down Expand Up @@ -260,9 +270,9 @@ def filter_keywords_for_session(session):
if group.state_id == 'bof':
keywords.add('bof')
keywords.add(group.acronym.lower())
token = session.docname_token_only_for_multiple()
if token is not None:
keywords.add(group.acronym.lower() + "-" + token)
specific_kw = filter_keyword_for_specific_session(session)
if specific_kw is not None:
keywords.add(specific_kw)
area = getattr(group, 'historic_parent', group.parent)

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

def filter_keyword_for_specific_session(session):
"""Get keyword that identifies a specific session
Returns None if the session cannot be selected individually.
"""
group = getattr(session, 'historic_group', session.group)
if group is None:
return None
kw = group.acronym.lower() # start with this
token = session.docname_token_only_for_multiple()
return kw if token is None else '{}-{}'.format(kw, token)

def read_session_file(type, num, doc):
# XXXX FIXME: the path fragment in the code below should be moved to
# settings.py. The *_PATH settings should be generalized to format()
Expand Down
8 changes: 8 additions & 0 deletions ietf/meeting/templatetags/agenda_custom_tags.py
Expand Up @@ -3,6 +3,7 @@


from django import template
from django.urls import reverse

register = template.Library()

Expand Down Expand Up @@ -60,3 +61,10 @@ def args(obj, arg):
obj.__callArg += [arg]
return obj

@register.simple_tag(name='webcal_url', takes_context=True)
def webcal_url(context, viewname, *args, **kwargs):
"""webcal URL for a view"""
return 'webcal://{}{}'.format(
context.request.get_host(),
reverse(viewname, args=args, kwargs=kwargs)
)
65 changes: 63 additions & 2 deletions ietf/meeting/tests_js.py
Expand Up @@ -1355,7 +1355,65 @@ def __call__(self, driver):
wait.until(in_iframe_href('tz=america/halifax', 'weekview'))
except:
self.fail('iframe href not updated to contain selected time zone')


def test_agenda_session_selection(self):
wait = WebDriverWait(self.driver, 2)
url = self.absreverse('ietf.meeting.views.agenda_personalize', kwargs={'num': self.meeting.number})
self.driver.get(url)

# Verify that elements are all updated when the filters change. That the correct elements
# have the appropriate classes is a separate test.
elements_to_check = self.driver.find_elements_by_css_selector('.agenda-link.filterable')
self.assertGreater(len(elements_to_check), 0, 'No elements with agenda links to update were found')

self.assertFalse(
any(checkbox.is_selected()
for checkbox in self.driver.find_elements_by_css_selector(
'input.checkbox[name="selected-sessions"]')),
'Sessions were selected before being clicked',
)

mars_checkbox = self.driver.find_element_by_css_selector('input[type="checkbox"][name="selected-sessions"][data-filter-item="mars"]')
break_checkbox = self.driver.find_element_by_css_selector('input[type="checkbox"][name="selected-sessions"][data-filter-item="secretariat-sessb"]')
registration_checkbox = self.driver.find_element_by_css_selector('input[type="checkbox"][name="selected-sessions"][data-filter-item="secretariat-sessa"]')
secretariat_button = self.driver.find_element_by_css_selector('button[data-filter-item="secretariat"]')

mars_checkbox.click() # select mars session
try:
wait.until(
lambda driver: all('?show=mars' in el.get_attribute('href') for el in elements_to_check)
)
except TimeoutException:
self.fail('Some agenda links were not updated when mars session was selected')
self.assertTrue(mars_checkbox.is_selected(), 'mars session checkbox was not selected after being clicked')
self.assertFalse(break_checkbox.is_selected(), 'break checkbox was selected without being clicked')
self.assertFalse(registration_checkbox.is_selected(), 'registration checkbox was selected without being clicked')

mars_checkbox.click() # deselect mars session
try:
wait.until(
lambda driver: not any('?show=mars' in el.get_attribute('href') for el in elements_to_check)
)
except TimeoutException:
self.fail('Some agenda links were not updated when mars session was de-selected')
self.assertFalse(mars_checkbox.is_selected(), 'mars session checkbox was still selected after being clicked')
self.assertFalse(break_checkbox.is_selected(), 'break checkbox was selected without being clicked')
self.assertFalse(registration_checkbox.is_selected(), 'registration checkbox was selected without being clicked')

secretariat_button.click() # turn on all secretariat sessions
break_checkbox.click() # also select the break

try:
wait.until(
lambda driver: all(
'?show=secretariat&hide=secretariat-sessb' in el.get_attribute('href')
for el in elements_to_check
))
except TimeoutException:
self.fail('Some agenda links were not updated when secretariat group but not break was selected')
self.assertFalse(mars_checkbox.is_selected(), 'mars session checkbox was unexpectedly selected')
self.assertFalse(break_checkbox.is_selected(), 'break checkbox was unexpectedly selected')
self.assertTrue(registration_checkbox.is_selected(), 'registration checkbox was expected to be selected')

@ifSeleniumEnabled
class WeekviewTests(IetfSeleniumTestCase):
Expand Down Expand Up @@ -1693,7 +1751,10 @@ def do_upcoming_view_filter_test(self, querystring, visible_meetings=()):
self.assert_upcoming_view_filter_matches_ics_filter(querystring)

# Check the ical links
simplified_querystring = querystring.replace(' ', '%20') # encode spaces'
simplified_querystring = querystring.replace(' ', '') # remove spaces
if simplified_querystring in ['?show=', '?hide=', '?show=&hide=']:
simplified_querystring = '' # these empty querystrings will be dropped (not an exhaustive list)

ics_link = self.driver.find_element_by_link_text('Download as .ics')
self.assertIn(simplified_querystring, ics_link.get_attribute('href'))
webcal_link = self.driver.find_element_by_link_text('Subscribe with webcal')
Expand Down
58 changes: 58 additions & 0 deletions ietf/meeting/tests_views.py
Expand Up @@ -35,6 +35,7 @@
from ietf.meeting.helpers import send_interim_approval_request
from ietf.meeting.helpers import send_interim_meeting_cancellation_notice, send_interim_session_cancellation_notice
from ietf.meeting.helpers import send_interim_minutes_reminder, populate_important_dates, update_important_dates
from ietf.meeting.helpers import filter_keyword_for_specific_session
from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignment, Schedule, SessionPresentation, SlideSubmission, SchedulingEvent, Room, Constraint, ConstraintName
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting, make_interim_test_data
from ietf.meeting.utils import finalize, condition_slide_order
Expand Down Expand Up @@ -375,6 +376,63 @@ def test_agenda_week_view(self):
self.assertEqual(r_with_tz.status_code,200)
self.assertEqual(r.content, r_with_tz.content)

def test_agenda_personalize(self):
"""Session selection page should have a checkbox for each session with appropriate keywords"""
meeting = make_meeting_test_data()
url = urlreverse("ietf.meeting.views.agenda_personalize",kwargs=dict(num=meeting.number))
r = self.client.get(url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)
for assignment in SchedTimeSessAssignment.objects.filter(
schedule__in=[meeting.schedule, meeting.schedule.base],
timeslot__type__private=False,
):
row = q('#row-{}'.format(assignment.slug()))
self.assertIsNotNone(row, 'No row for assignment {}'.format(assignment))
checkboxes = row('input[type="checkbox"][name="selected-sessions"]')
self.assertEqual(len(checkboxes), 1,
'Row for assignment {} does not have a checkbox input'.format(assignment))
checkbox = checkboxes.eq(0)
self.assertEqual(
checkbox.attr('data-filter-item'),
filter_keyword_for_specific_session(assignment.session),
)

def test_agenda_personalize_updates_urls(self):
"""The correct URLs should be updated when filter settings change on the personalize agenda view
Tests that the expected elements have the necessary classes. The actual update of these fields
is tested in the JS tests
"""
meeting = make_meeting_test_data()
url = urlreverse("ietf.meeting.views.agenda_personalize",kwargs=dict(num=meeting.number))
r = self.client.get(url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)

# Find all the elements expected to be updated
expected_elements = []
nav_tab_anchors = q('ul.nav.nav-tabs > li > a')
for anchor in nav_tab_anchors.items():
text = anchor.text().strip()
if text in ['Agenda', 'UTC Agenda', 'Select Sessions']:
expected_elements.append(anchor)
for btn in q('.buttonlist a.btn').items():
text = btn.text().strip()
if text in ['View customized agenda', 'Download as .ics', 'Subscribe with webcal']:
expected_elements.append(btn)

# Check that all the expected elements have the correct classes
for elt in expected_elements:
self.assertTrue(elt.has_class('agenda-link'))
self.assertTrue(elt.has_class('filterable'))

# Finally, check that there are no unexpected elements marked to be updated.
# If there are, they should be added to the test above.
self.assertEqual(len(expected_elements),
len(q('.agenda-link.filterable')),
'Unexpected elements updated')

@override_settings(MEETING_MATERIALS_SERVE_LOCALLY=False, MEETING_DOC_HREFS = settings.MEETING_DOC_CDN_HREFS)
def test_materials_through_cdn(self):
meeting = make_meeting_test_data(create_interims=True)
Expand Down
1 change: 1 addition & 0 deletions ietf/meeting/urls.py
Expand Up @@ -47,6 +47,7 @@
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]+)/ics$', views.agenda_by_type_ics),
url(r'^agenda/personalize', views.agenda_personalize),
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/diff/$', views.diff_schedules),
Expand Down

0 comments on commit 82ad040

Please sign in to comment.