Skip to content

Commit 4c709bb

Browse files
Add filtering support (but no UI) to upcoming meeting views; add a group attribute to agenda view rows to avoid ambiguity when selecting rows to show/hide. Branch ready for merge.
- Legacy-Id: 18458
1 parent d9d5234 commit 4c709bb

5 files changed

Lines changed: 330 additions & 109 deletions

File tree

ietf/meeting/test_data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ def make_interim_test_data():
219219
ad = Person.objects.get(user__username='ad')
220220
RoleFactory(group=area,person=ad,name_id='ad')
221221
mars = GroupFactory(acronym='mars',parent=area,name='Martian Special Interest Group')
222-
ames = GroupFactory(acronym='ames',parent=area)
222+
ames = GroupFactory(acronym='ames',parent=area,name='Asteroid Mining Equipment Standardization Group')
223223
RoleFactory(group=mars,person__user__username='marschairman',name_id='chair')
224224
RoleFactory(group=ames,person__user__username='ameschairman',name_id='chair')
225225

ietf/meeting/tests_views.py

Lines changed: 228 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from django.contrib.auth.models import User
2323
from django.test import Client, override_settings
2424
from django.db.models import F
25+
from django.http import QueryDict
2526

2627
import debug # pyflakes:ignore
2728

@@ -37,7 +38,7 @@
3738
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting, make_interim_test_data
3839
from ietf.meeting.utils import finalize, condition_slide_order
3940
from ietf.meeting.utils import add_event_info_to_session_qs
40-
from ietf.meeting.views import session_draft_list
41+
from ietf.meeting.views import session_draft_list, parse_agenda_filter_params
4142
from ietf.name.models import SessionStatusName, ImportantDateName, RoleName
4243
from ietf.utils.decorators import skip_coverage
4344
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
@@ -61,6 +62,31 @@
6162
"location indicated in settings.py.")
6263
print(" "+skip_message)
6364

65+
66+
def assert_ical_response_is_valid(test_inst, response, expected_event_summaries=None, expected_event_count=None):
67+
"""Validate an HTTP response containing iCal data
68+
69+
Based on RFC2445, but not exhaustive by any means. Assumes a single iCalendar object.
70+
"""
71+
test_inst.assertEqual(response.get('Content-Type'), "text/calendar")
72+
73+
# Validate iCalendar object
74+
test_inst.assertContains(response, 'BEGIN:VCALENDAR', count=1)
75+
test_inst.assertContains(response, 'END:VCALENDAR', count=1)
76+
test_inst.assertContains(response, 'PRODID:', count=1)
77+
test_inst.assertContains(response, 'VERSION', count=1)
78+
79+
# Validate event objects
80+
if expected_event_summaries is not None:
81+
for summary in expected_event_summaries:
82+
test_inst.assertContains(response, 'SUMMARY:' + summary)
83+
84+
if expected_event_count is not None:
85+
test_inst.assertContains(response, 'BEGIN:VEVENT', count=expected_event_count)
86+
test_inst.assertContains(response, 'END:VEVENT', count=expected_event_count)
87+
test_inst.assertContains(response, 'UID', count=expected_event_count)
88+
89+
6490
class MeetingTests(TestCase):
6591
def setUp(self):
6692
self.materials_dir = self.tempdir('materials')
@@ -580,28 +606,6 @@ def test_important_dates(self):
580606
post_date = meeting.importantdate_set.get(name=idn).date
581607
self.assertEqual(pre_date, post_date+datetime.timedelta(days=1))
582608

583-
def assert_ical_response_is_valid(self, response, expected_event_summaries=None, expected_event_count=None):
584-
"""Validate an HTTP response containing iCal data
585-
586-
Based on RFC2445, but not exhaustive by any means. Assumes a single iCalendar object.
587-
"""
588-
self.assertEqual(response.get('Content-Type'), "text/calendar")
589-
590-
# Validate iCalendar object
591-
self.assertContains(response, 'BEGIN:VCALENDAR', count=1)
592-
self.assertContains(response, 'END:VCALENDAR', count=1)
593-
self.assertContains(response, 'PRODID:', count=1)
594-
self.assertContains(response, 'VERSION', count=1)
595-
596-
# Validate event objects
597-
if expected_event_count is None:
598-
expected_event_count = len(expected_event_summaries)
599-
self.assertContains(response, 'BEGIN:VEVENT', count=expected_event_count)
600-
self.assertContains(response, 'END:VEVENT', count=expected_event_count)
601-
self.assertContains(response, 'UID', count=expected_event_count)
602-
for summary in expected_event_summaries:
603-
self.assertContains(response, 'SUMMARY:' + summary)
604-
605609
def test_group_ical(self):
606610
meeting = make_meeting_test_data()
607611
s1 = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
@@ -614,17 +618,18 @@ def test_group_ical(self):
614618
#
615619
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number, 'acronym':s1.group.acronym, })
616620
r = self.client.get(url)
617-
self.assert_ical_response_is_valid(r,
618-
expected_event_summaries=['mars - Martian Special Interest Group'],
619-
expected_event_count=2)
621+
assert_ical_response_is_valid(self,
622+
r,
623+
expected_event_summaries=['mars - Martian Special Interest Group'],
624+
expected_event_count=2)
620625
self.assertContains(r, t1.time.strftime('%Y%m%dT%H%M%S'))
621626
self.assertContains(r, t2.time.strftime('%Y%m%dT%H%M%S'))
622627
#
623628
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number, 'session_id':s1.id, })
624629
r = self.client.get(url)
625-
self.assert_ical_response_is_valid(r,
626-
expected_event_summaries=['mars - Martian Special Interest Group'],
627-
expected_event_count=1)
630+
assert_ical_response_is_valid(self, r,
631+
expected_event_summaries=['mars - Martian Special Interest Group'],
632+
expected_event_count=1)
628633
self.assertContains(r, t1.time.strftime('%Y%m%dT%H%M%S'))
629634
self.assertNotContains(r, t2.time.strftime('%Y%m%dT%H%M%S'))
630635

@@ -652,7 +657,36 @@ def test_meeting_agenda_has_static_ical_links(self):
652657
for g in groups:
653658
if g.parent_id is not None:
654659
self.assertIn('%s?show=%s' % (ical_url, g.parent.acronym.lower()), content)
655-
660+
661+
def test_parse_agenda_filter_params(self):
662+
def _r(show=(), hide=(), showtypes=(), hidetypes=()):
663+
"""Helper to create expected result dict"""
664+
return dict(show=set(show), hide=set(hide), showtypes=set(showtypes), hidetypes=set(hidetypes))
665+
666+
self.assertIsNone(parse_agenda_filter_params(QueryDict('')))
667+
668+
self.assertRaises(ValueError, parse_agenda_filter_params, QueryDict('unknown')) # unknown param
669+
self.assertRaises(ValueError, parse_agenda_filter_params, QueryDict('unknown=x')) # unknown param
670+
671+
# test valid combos (not exhaustive)
672+
for qstr, expected in (
673+
('show=', _r()), ('hide=', _r()), ('showtypes=', _r()), ('hidetypes=', _r()),
674+
('show=x', _r(show=['x'])), ('hide=x', _r(hide=['x'])),
675+
('showtypes=x', _r(showtypes=['x'])), ('hidetypes=x', _r(hidetypes=['x'])),
676+
('show=x,y,z', _r(show=['x','y','z'])),
677+
('hide=x,y,z', _r(hide=['x','y','z'])),
678+
('showtypes=x,y,z', _r(showtypes=['x','y','z'])),
679+
('hidetypes=x,y,z', _r(hidetypes=['x','y','z'])),
680+
('show=a&hide=a', _r(show=['a'], hide=['a'])),
681+
('show=a&hide=b', _r(show=['a'], hide=['b'])),
682+
('show=a&hide=b&showtypes=c&hidetypes=d', _r(show=['a'], hide=['b'], showtypes=['c'], hidetypes=['d'])),
683+
):
684+
self.assertEqual(
685+
parse_agenda_filter_params(QueryDict(qstr)),
686+
expected,
687+
'Parsed "%s" incorrectly' % qstr,
688+
)
689+
656690
def test_ical_filter_invalid_syntaxes(self):
657691
meeting = make_meeting_test_data()
658692
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number})
@@ -667,7 +701,10 @@ def do_ical_filter_test(self, meeting, querystring, expected_session_summaries):
667701
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number})
668702
r = self.client.get(url + querystring)
669703
self.assertEqual(r.status_code, 200)
670-
self.assert_ical_response_is_valid(r, expected_event_summaries=expected_session_summaries)
704+
assert_ical_response_is_valid(self,
705+
r,
706+
expected_event_summaries=expected_session_summaries,
707+
expected_event_count=len(expected_session_summaries))
671708

672709
def test_ical_filter_default(self):
673710
meeting = make_meeting_test_data()
@@ -1799,37 +1836,179 @@ def test_past(self):
17991836
#self.assertIn('CANCELLED', q('[id*="'+id+'"]').text())
18001837
self.assertIn('CANCELLED', q('tr>td>a>span').text())
18011838

1802-
def test_upcoming(self):
1803-
make_meeting_test_data(create_interims=True)
1839+
def do_upcoming_test(self, querystring=None, create_meeting=True):
1840+
if create_meeting:
1841+
make_meeting_test_data(create_interims=True)
18041842
url = urlreverse("ietf.meeting.views.upcoming")
1843+
if querystring is not None:
1844+
url += '?' + querystring
1845+
18051846
today = datetime.date.today()
18061847
add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', group__acronym='mars')).filter(current_status='apprw').first()
1807-
mars_interim = add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', meeting__date__gt=today, group__acronym='mars')).filter(current_status='sched').first().meeting
1808-
ames_interim = add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', meeting__date__gt=today, group__acronym='ames')).filter(current_status='canceled').first().meeting
1809-
r = self.client.get(url)
1810-
self.assertContains(r, mars_interim.number)
1811-
self.assertContains(r, ames_interim.number)
1848+
interims = dict(
1849+
mars=add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', meeting__date__gt=today, group__acronym='mars')).filter(current_status='sched').first().meeting,
1850+
ames=add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', meeting__date__gt=today, group__acronym='ames')).filter(current_status='canceled').first().meeting,
1851+
)
1852+
self.check_interim_tabs(url)
1853+
return self.client.get(url), interims
1854+
1855+
def test_upcoming(self):
1856+
r, interims = self.do_upcoming_test()
1857+
self.assertContains(r, interims['mars'].number)
1858+
self.assertContains(r, interims['ames'].number)
18121859
self.assertContains(r, 'IETF 72')
18131860
# cancelled session
18141861
q = PyQuery(r.content)
18151862
self.assertIn('CANCELLED', q('tr>td.text-right>span').text())
1816-
self.check_interim_tabs(url)
18171863

1818-
def test_upcoming_ical(self):
1864+
def test_upcoming_filter_show(self):
1865+
r, interims = self.do_upcoming_test('show=ames')
1866+
self.assertNotContains(r, interims['mars'].number)
1867+
self.assertContains(r, interims['ames'].number)
1868+
self.assertContains(r, 'IETF 72')
1869+
# cancelled session
1870+
q = PyQuery(r.content)
1871+
self.assertIn('CANCELLED', q('tr>td.text-right>span').text())
1872+
1873+
def test_upcoming_filter_show_area(self):
18191874
make_meeting_test_data(create_interims=True)
1875+
area = Group.objects.get(acronym='mars').parent
1876+
self.assertEqual(area,
1877+
Group.objects.get(acronym='ames').parent,
1878+
'The mars and ames groups have different areas; this breaks this test')
1879+
r, interims = self.do_upcoming_test('show=%s' % area.acronym, create_meeting=False)
1880+
self.assertContains(r, interims['mars'].number)
1881+
self.assertContains(r, interims['ames'].number)
1882+
self.assertContains(r, 'IETF 72')
1883+
1884+
def test_upcoming_filter_hide(self):
1885+
r, interims = self.do_upcoming_test('hide=mars')
1886+
self.assertNotContains(r, interims['mars'].number)
1887+
self.assertNotContains(r, interims['ames'].number)
1888+
self.assertContains(r, 'IETF 72')
1889+
1890+
def test_upcoming_filter_show_and_hide(self):
1891+
r, interims = self.do_upcoming_test('show=mars,ames&hide=ames')
1892+
self.assertContains(r, interims['mars'].number)
1893+
self.assertNotContains(r, interims['ames'].number)
1894+
self.assertContains(r, 'IETF 72')
1895+
1896+
def do_upcoming_ical_test(self, querystring=None):
1897+
make_meeting_test_data(create_interims=True)
1898+
1899+
# Create a group with a plenary interim session for testing type filters
1900+
somegroup = GroupFactory(acronym='sg', name='Some Group')
1901+
sg_interim = make_interim_meeting(somegroup, datetime.date.today() + datetime.timedelta(days=20))
1902+
sg_sess = sg_interim.session_set.first()
1903+
sg_slot = sg_sess.timeslotassignments.first().timeslot
1904+
sg_sess.type_id = 'plenary'
1905+
sg_slot.type_id = 'plenary'
1906+
sg_sess.save()
1907+
sg_slot.save()
1908+
18201909
url = urlreverse("ietf.meeting.views.upcoming_ical")
1910+
if querystring is not None:
1911+
url += '?' + querystring
18211912
r = self.client.get(url)
18221913
self.assertEqual(r.status_code, 200)
1823-
self.assertEqual(r.get('Content-Type'), "text/calendar")
1824-
self.assertEqual(r.content.count(b'UID'), 8)
1825-
# check filtered output
1826-
url = url + '?filters=mars'
1827-
r = self.client.get(url)
1828-
self.assertEqual(r.status_code, 200)
1829-
self.assertEqual(r.get('Content-Type'), "text/calendar")
1830-
# print r.content
1831-
self.assertEqual(r.content.count(b'UID'), 2)
1914+
return r
18321915

1916+
def test_upcoming_ical(self):
1917+
r = self.do_upcoming_ical_test()
1918+
print(r.content.decode())
1919+
assert_ical_response_is_valid(self, r,
1920+
expected_event_summaries=[
1921+
'ames - Asteroid Mining Equipment Standardization Group',
1922+
'mars - Martian Special Interest Group',
1923+
'sg - Some Group',
1924+
],
1925+
expected_event_count=9)
1926+
1927+
def test_upcoming_ical_filter_show(self):
1928+
r = self.do_upcoming_ical_test('show=mars,ames')
1929+
assert_ical_response_is_valid(self, r,
1930+
expected_event_summaries=[
1931+
'mars - Martian Special Interest Group',
1932+
'ames - Asteroid Mining Equipment Standardization Group',
1933+
])
1934+
1935+
def test_upcoming_ical_filter_hide(self):
1936+
r = self.do_upcoming_ical_test('hide=mars')
1937+
assert_ical_response_is_valid(self, r, expected_event_summaries=[])
1938+
1939+
def test_upcoming_ical_filter_show_and_hide(self):
1940+
r = self.do_upcoming_ical_test('show=mars,ames&hide=mars')
1941+
assert_ical_response_is_valid(self, r,
1942+
expected_event_summaries=[
1943+
'ames - Asteroid Mining Equipment Standardization Group',
1944+
])
1945+
1946+
def test_upcoming_ical_filter_showtypes(self):
1947+
r = self.do_upcoming_ical_test('showtypes=regular')
1948+
assert_ical_response_is_valid(self, r,
1949+
expected_event_summaries=[
1950+
'mars - Martian Special Interest Group',
1951+
'ames - Asteroid Mining Equipment Standardization Group',
1952+
])
1953+
1954+
def test_upcoming_ical_filter_hidetypes(self):
1955+
r = self.do_upcoming_ical_test('hidetypes=regular')
1956+
assert_ical_response_is_valid(self, r, expected_event_summaries=[])
1957+
1958+
def test_upcoming_ical_filter_showtypes_and_hidetypes(self):
1959+
r = self.do_upcoming_ical_test('showtypes=plenary,regular&hidetypes=regular')
1960+
assert_ical_response_is_valid(self, r,
1961+
expected_event_summaries=[
1962+
'sg - Some Group',
1963+
])
1964+
1965+
def test_upcoming_ical_filter_show_and_showtypes(self):
1966+
r = self.do_upcoming_ical_test('show=mars&showtypes=plenary')
1967+
assert_ical_response_is_valid(self, r,
1968+
expected_event_summaries=[
1969+
'mars - Martian Special Interest Group',
1970+
'sg - Some Group',
1971+
])
1972+
1973+
def test_upcoming_ical_filter_show_and_hidetypes(self):
1974+
r = self.do_upcoming_ical_test('show=mars,sg&showtypes=regular')
1975+
assert_ical_response_is_valid(self, r,
1976+
expected_event_summaries=[
1977+
'sg - Some Group',
1978+
])
1979+
1980+
def test_upcoming_ical_filter_hide_and_showtypes(self):
1981+
r = self.do_upcoming_ical_test('hide=mars&showtypes=regular')
1982+
assert_ical_response_is_valid(self, r,
1983+
expected_event_summaries=[
1984+
'ames - Asteroid Mining Equipment Standardization Group',
1985+
])
1986+
1987+
def test_upcoming_ical_filter_hide_and_hidetypes(self):
1988+
r = self.do_upcoming_ical_test('hide=mars&hidetypes=regular')
1989+
assert_ical_response_is_valid(self, r, expected_event_summaries=[])
1990+
1991+
def test_upcoming_ical_filter_show_hide_and_showtypes(self):
1992+
r = self.do_upcoming_ical_test('show=ames&hide=mars&showtypes=regular,plenary')
1993+
assert_ical_response_is_valid(self, r,
1994+
expected_event_summaries=[
1995+
'ames - Asteroid Mining Equipment Standardization Group',
1996+
'sg - Some Group',
1997+
])
1998+
1999+
def test_upcoming_ical_filter_show_hide_and_hidetypes(self):
2000+
r = self.do_upcoming_ical_test('show=ames,sg&hide=mars&hidetypes=regular')
2001+
assert_ical_response_is_valid(self, r,
2002+
expected_event_summaries=[
2003+
'sg - Some Group',
2004+
])
2005+
2006+
def test_upcoming_ical_filter_all_params(self):
2007+
r = self.do_upcoming_ical_test('show=sg&hide=ames&showtypes=regular&hidetypes=plenary')
2008+
assert_ical_response_is_valid(self, r,
2009+
expected_event_summaries=[
2010+
'mars - Martian Special Interest Group',
2011+
])
18332012

18342013
def test_upcoming_json(self):
18352014
make_meeting_test_data(create_interims=True)

0 commit comments

Comments
 (0)