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
This commit is contained in:
parent
d9d5234217
commit
4c709bbfa5
|
@ -219,7 +219,7 @@ def make_interim_test_data():
|
|||
ad = Person.objects.get(user__username='ad')
|
||||
RoleFactory(group=area,person=ad,name_id='ad')
|
||||
mars = GroupFactory(acronym='mars',parent=area,name='Martian Special Interest Group')
|
||||
ames = GroupFactory(acronym='ames',parent=area)
|
||||
ames = GroupFactory(acronym='ames',parent=area,name='Asteroid Mining Equipment Standardization Group')
|
||||
RoleFactory(group=mars,person__user__username='marschairman',name_id='chair')
|
||||
RoleFactory(group=ames,person__user__username='ameschairman',name_id='chair')
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ from django.conf import settings
|
|||
from django.contrib.auth.models import User
|
||||
from django.test import Client, override_settings
|
||||
from django.db.models import F
|
||||
from django.http import QueryDict
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -37,7 +38,7 @@ from ietf.meeting.models import Session, TimeSlot, Meeting, SchedTimeSessAssignm
|
|||
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
|
||||
from ietf.meeting.utils import add_event_info_to_session_qs
|
||||
from ietf.meeting.views import session_draft_list
|
||||
from ietf.meeting.views import session_draft_list, parse_agenda_filter_params
|
||||
from ietf.name.models import SessionStatusName, ImportantDateName, RoleName
|
||||
from ietf.utils.decorators import skip_coverage
|
||||
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
||||
|
@ -61,6 +62,31 @@ else:
|
|||
"location indicated in settings.py.")
|
||||
print(" "+skip_message)
|
||||
|
||||
|
||||
def assert_ical_response_is_valid(test_inst, response, expected_event_summaries=None, expected_event_count=None):
|
||||
"""Validate an HTTP response containing iCal data
|
||||
|
||||
Based on RFC2445, but not exhaustive by any means. Assumes a single iCalendar object.
|
||||
"""
|
||||
test_inst.assertEqual(response.get('Content-Type'), "text/calendar")
|
||||
|
||||
# Validate iCalendar object
|
||||
test_inst.assertContains(response, 'BEGIN:VCALENDAR', count=1)
|
||||
test_inst.assertContains(response, 'END:VCALENDAR', count=1)
|
||||
test_inst.assertContains(response, 'PRODID:', count=1)
|
||||
test_inst.assertContains(response, 'VERSION', count=1)
|
||||
|
||||
# Validate event objects
|
||||
if expected_event_summaries is not None:
|
||||
for summary in expected_event_summaries:
|
||||
test_inst.assertContains(response, 'SUMMARY:' + summary)
|
||||
|
||||
if expected_event_count is not None:
|
||||
test_inst.assertContains(response, 'BEGIN:VEVENT', count=expected_event_count)
|
||||
test_inst.assertContains(response, 'END:VEVENT', count=expected_event_count)
|
||||
test_inst.assertContains(response, 'UID', count=expected_event_count)
|
||||
|
||||
|
||||
class MeetingTests(TestCase):
|
||||
def setUp(self):
|
||||
self.materials_dir = self.tempdir('materials')
|
||||
|
@ -580,28 +606,6 @@ class MeetingTests(TestCase):
|
|||
post_date = meeting.importantdate_set.get(name=idn).date
|
||||
self.assertEqual(pre_date, post_date+datetime.timedelta(days=1))
|
||||
|
||||
def assert_ical_response_is_valid(self, response, expected_event_summaries=None, expected_event_count=None):
|
||||
"""Validate an HTTP response containing iCal data
|
||||
|
||||
Based on RFC2445, but not exhaustive by any means. Assumes a single iCalendar object.
|
||||
"""
|
||||
self.assertEqual(response.get('Content-Type'), "text/calendar")
|
||||
|
||||
# Validate iCalendar object
|
||||
self.assertContains(response, 'BEGIN:VCALENDAR', count=1)
|
||||
self.assertContains(response, 'END:VCALENDAR', count=1)
|
||||
self.assertContains(response, 'PRODID:', count=1)
|
||||
self.assertContains(response, 'VERSION', count=1)
|
||||
|
||||
# Validate event objects
|
||||
if expected_event_count is None:
|
||||
expected_event_count = len(expected_event_summaries)
|
||||
self.assertContains(response, 'BEGIN:VEVENT', count=expected_event_count)
|
||||
self.assertContains(response, 'END:VEVENT', count=expected_event_count)
|
||||
self.assertContains(response, 'UID', count=expected_event_count)
|
||||
for summary in expected_event_summaries:
|
||||
self.assertContains(response, 'SUMMARY:' + summary)
|
||||
|
||||
def test_group_ical(self):
|
||||
meeting = make_meeting_test_data()
|
||||
s1 = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
|
||||
|
@ -614,17 +618,18 @@ class MeetingTests(TestCase):
|
|||
#
|
||||
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number, 'acronym':s1.group.acronym, })
|
||||
r = self.client.get(url)
|
||||
self.assert_ical_response_is_valid(r,
|
||||
expected_event_summaries=['mars - Martian Special Interest Group'],
|
||||
expected_event_count=2)
|
||||
assert_ical_response_is_valid(self,
|
||||
r,
|
||||
expected_event_summaries=['mars - Martian Special Interest Group'],
|
||||
expected_event_count=2)
|
||||
self.assertContains(r, t1.time.strftime('%Y%m%dT%H%M%S'))
|
||||
self.assertContains(r, t2.time.strftime('%Y%m%dT%H%M%S'))
|
||||
#
|
||||
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number, 'session_id':s1.id, })
|
||||
r = self.client.get(url)
|
||||
self.assert_ical_response_is_valid(r,
|
||||
expected_event_summaries=['mars - Martian Special Interest Group'],
|
||||
expected_event_count=1)
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=['mars - Martian Special Interest Group'],
|
||||
expected_event_count=1)
|
||||
self.assertContains(r, t1.time.strftime('%Y%m%dT%H%M%S'))
|
||||
self.assertNotContains(r, t2.time.strftime('%Y%m%dT%H%M%S'))
|
||||
|
||||
|
@ -652,7 +657,36 @@ class MeetingTests(TestCase):
|
|||
for g in groups:
|
||||
if g.parent_id is not None:
|
||||
self.assertIn('%s?show=%s' % (ical_url, g.parent.acronym.lower()), content)
|
||||
|
||||
|
||||
def test_parse_agenda_filter_params(self):
|
||||
def _r(show=(), hide=(), showtypes=(), hidetypes=()):
|
||||
"""Helper to create expected result dict"""
|
||||
return dict(show=set(show), hide=set(hide), showtypes=set(showtypes), hidetypes=set(hidetypes))
|
||||
|
||||
self.assertIsNone(parse_agenda_filter_params(QueryDict('')))
|
||||
|
||||
self.assertRaises(ValueError, parse_agenda_filter_params, QueryDict('unknown')) # unknown param
|
||||
self.assertRaises(ValueError, parse_agenda_filter_params, QueryDict('unknown=x')) # unknown param
|
||||
|
||||
# test valid combos (not exhaustive)
|
||||
for qstr, expected in (
|
||||
('show=', _r()), ('hide=', _r()), ('showtypes=', _r()), ('hidetypes=', _r()),
|
||||
('show=x', _r(show=['x'])), ('hide=x', _r(hide=['x'])),
|
||||
('showtypes=x', _r(showtypes=['x'])), ('hidetypes=x', _r(hidetypes=['x'])),
|
||||
('show=x,y,z', _r(show=['x','y','z'])),
|
||||
('hide=x,y,z', _r(hide=['x','y','z'])),
|
||||
('showtypes=x,y,z', _r(showtypes=['x','y','z'])),
|
||||
('hidetypes=x,y,z', _r(hidetypes=['x','y','z'])),
|
||||
('show=a&hide=a', _r(show=['a'], hide=['a'])),
|
||||
('show=a&hide=b', _r(show=['a'], hide=['b'])),
|
||||
('show=a&hide=b&showtypes=c&hidetypes=d', _r(show=['a'], hide=['b'], showtypes=['c'], hidetypes=['d'])),
|
||||
):
|
||||
self.assertEqual(
|
||||
parse_agenda_filter_params(QueryDict(qstr)),
|
||||
expected,
|
||||
'Parsed "%s" incorrectly' % qstr,
|
||||
)
|
||||
|
||||
def test_ical_filter_invalid_syntaxes(self):
|
||||
meeting = make_meeting_test_data()
|
||||
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number})
|
||||
|
@ -667,7 +701,10 @@ class MeetingTests(TestCase):
|
|||
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number})
|
||||
r = self.client.get(url + querystring)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assert_ical_response_is_valid(r, expected_event_summaries=expected_session_summaries)
|
||||
assert_ical_response_is_valid(self,
|
||||
r,
|
||||
expected_event_summaries=expected_session_summaries,
|
||||
expected_event_count=len(expected_session_summaries))
|
||||
|
||||
def test_ical_filter_default(self):
|
||||
meeting = make_meeting_test_data()
|
||||
|
@ -1799,37 +1836,179 @@ class InterimTests(TestCase):
|
|||
#self.assertIn('CANCELLED', q('[id*="'+id+'"]').text())
|
||||
self.assertIn('CANCELLED', q('tr>td>a>span').text())
|
||||
|
||||
def test_upcoming(self):
|
||||
make_meeting_test_data(create_interims=True)
|
||||
def do_upcoming_test(self, querystring=None, create_meeting=True):
|
||||
if create_meeting:
|
||||
make_meeting_test_data(create_interims=True)
|
||||
url = urlreverse("ietf.meeting.views.upcoming")
|
||||
if querystring is not None:
|
||||
url += '?' + querystring
|
||||
|
||||
today = datetime.date.today()
|
||||
add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', group__acronym='mars')).filter(current_status='apprw').first()
|
||||
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
|
||||
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
|
||||
r = self.client.get(url)
|
||||
self.assertContains(r, mars_interim.number)
|
||||
self.assertContains(r, ames_interim.number)
|
||||
interims = dict(
|
||||
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,
|
||||
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,
|
||||
)
|
||||
self.check_interim_tabs(url)
|
||||
return self.client.get(url), interims
|
||||
|
||||
def test_upcoming(self):
|
||||
r, interims = self.do_upcoming_test()
|
||||
self.assertContains(r, interims['mars'].number)
|
||||
self.assertContains(r, interims['ames'].number)
|
||||
self.assertContains(r, 'IETF 72')
|
||||
# cancelled session
|
||||
q = PyQuery(r.content)
|
||||
self.assertIn('CANCELLED', q('tr>td.text-right>span').text())
|
||||
self.check_interim_tabs(url)
|
||||
|
||||
def test_upcoming_filter_show(self):
|
||||
r, interims = self.do_upcoming_test('show=ames')
|
||||
self.assertNotContains(r, interims['mars'].number)
|
||||
self.assertContains(r, interims['ames'].number)
|
||||
self.assertContains(r, 'IETF 72')
|
||||
# cancelled session
|
||||
q = PyQuery(r.content)
|
||||
self.assertIn('CANCELLED', q('tr>td.text-right>span').text())
|
||||
|
||||
def test_upcoming_filter_show_area(self):
|
||||
make_meeting_test_data(create_interims=True)
|
||||
area = Group.objects.get(acronym='mars').parent
|
||||
self.assertEqual(area,
|
||||
Group.objects.get(acronym='ames').parent,
|
||||
'The mars and ames groups have different areas; this breaks this test')
|
||||
r, interims = self.do_upcoming_test('show=%s' % area.acronym, create_meeting=False)
|
||||
self.assertContains(r, interims['mars'].number)
|
||||
self.assertContains(r, interims['ames'].number)
|
||||
self.assertContains(r, 'IETF 72')
|
||||
|
||||
def test_upcoming_filter_hide(self):
|
||||
r, interims = self.do_upcoming_test('hide=mars')
|
||||
self.assertNotContains(r, interims['mars'].number)
|
||||
self.assertNotContains(r, interims['ames'].number)
|
||||
self.assertContains(r, 'IETF 72')
|
||||
|
||||
def test_upcoming_filter_show_and_hide(self):
|
||||
r, interims = self.do_upcoming_test('show=mars,ames&hide=ames')
|
||||
self.assertContains(r, interims['mars'].number)
|
||||
self.assertNotContains(r, interims['ames'].number)
|
||||
self.assertContains(r, 'IETF 72')
|
||||
|
||||
def do_upcoming_ical_test(self, querystring=None):
|
||||
make_meeting_test_data(create_interims=True)
|
||||
|
||||
# Create a group with a plenary interim session for testing type filters
|
||||
somegroup = GroupFactory(acronym='sg', name='Some Group')
|
||||
sg_interim = make_interim_meeting(somegroup, datetime.date.today() + datetime.timedelta(days=20))
|
||||
sg_sess = sg_interim.session_set.first()
|
||||
sg_slot = sg_sess.timeslotassignments.first().timeslot
|
||||
sg_sess.type_id = 'plenary'
|
||||
sg_slot.type_id = 'plenary'
|
||||
sg_sess.save()
|
||||
sg_slot.save()
|
||||
|
||||
url = urlreverse("ietf.meeting.views.upcoming_ical")
|
||||
if querystring is not None:
|
||||
url += '?' + querystring
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
return r
|
||||
|
||||
def test_upcoming_ical(self):
|
||||
make_meeting_test_data(create_interims=True)
|
||||
url = urlreverse("ietf.meeting.views.upcoming_ical")
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.get('Content-Type'), "text/calendar")
|
||||
self.assertEqual(r.content.count(b'UID'), 8)
|
||||
# check filtered output
|
||||
url = url + '?filters=mars'
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.get('Content-Type'), "text/calendar")
|
||||
# print r.content
|
||||
self.assertEqual(r.content.count(b'UID'), 2)
|
||||
r = self.do_upcoming_ical_test()
|
||||
print(r.content.decode())
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'ames - Asteroid Mining Equipment Standardization Group',
|
||||
'mars - Martian Special Interest Group',
|
||||
'sg - Some Group',
|
||||
],
|
||||
expected_event_count=9)
|
||||
|
||||
def test_upcoming_ical_filter_show(self):
|
||||
r = self.do_upcoming_ical_test('show=mars,ames')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'mars - Martian Special Interest Group',
|
||||
'ames - Asteroid Mining Equipment Standardization Group',
|
||||
])
|
||||
|
||||
def test_upcoming_ical_filter_hide(self):
|
||||
r = self.do_upcoming_ical_test('hide=mars')
|
||||
assert_ical_response_is_valid(self, r, expected_event_summaries=[])
|
||||
|
||||
def test_upcoming_ical_filter_show_and_hide(self):
|
||||
r = self.do_upcoming_ical_test('show=mars,ames&hide=mars')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'ames - Asteroid Mining Equipment Standardization Group',
|
||||
])
|
||||
|
||||
def test_upcoming_ical_filter_showtypes(self):
|
||||
r = self.do_upcoming_ical_test('showtypes=regular')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'mars - Martian Special Interest Group',
|
||||
'ames - Asteroid Mining Equipment Standardization Group',
|
||||
])
|
||||
|
||||
def test_upcoming_ical_filter_hidetypes(self):
|
||||
r = self.do_upcoming_ical_test('hidetypes=regular')
|
||||
assert_ical_response_is_valid(self, r, expected_event_summaries=[])
|
||||
|
||||
def test_upcoming_ical_filter_showtypes_and_hidetypes(self):
|
||||
r = self.do_upcoming_ical_test('showtypes=plenary,regular&hidetypes=regular')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'sg - Some Group',
|
||||
])
|
||||
|
||||
def test_upcoming_ical_filter_show_and_showtypes(self):
|
||||
r = self.do_upcoming_ical_test('show=mars&showtypes=plenary')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'mars - Martian Special Interest Group',
|
||||
'sg - Some Group',
|
||||
])
|
||||
|
||||
def test_upcoming_ical_filter_show_and_hidetypes(self):
|
||||
r = self.do_upcoming_ical_test('show=mars,sg&showtypes=regular')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'sg - Some Group',
|
||||
])
|
||||
|
||||
def test_upcoming_ical_filter_hide_and_showtypes(self):
|
||||
r = self.do_upcoming_ical_test('hide=mars&showtypes=regular')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'ames - Asteroid Mining Equipment Standardization Group',
|
||||
])
|
||||
|
||||
def test_upcoming_ical_filter_hide_and_hidetypes(self):
|
||||
r = self.do_upcoming_ical_test('hide=mars&hidetypes=regular')
|
||||
assert_ical_response_is_valid(self, r, expected_event_summaries=[])
|
||||
|
||||
def test_upcoming_ical_filter_show_hide_and_showtypes(self):
|
||||
r = self.do_upcoming_ical_test('show=ames&hide=mars&showtypes=regular,plenary')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'ames - Asteroid Mining Equipment Standardization Group',
|
||||
'sg - Some Group',
|
||||
])
|
||||
|
||||
def test_upcoming_ical_filter_show_hide_and_hidetypes(self):
|
||||
r = self.do_upcoming_ical_test('show=ames,sg&hide=mars&hidetypes=regular')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'sg - Some Group',
|
||||
])
|
||||
|
||||
def test_upcoming_ical_filter_all_params(self):
|
||||
r = self.do_upcoming_ical_test('show=sg&hide=ames&showtypes=regular&hidetypes=plenary')
|
||||
assert_ical_response_is_valid(self, r,
|
||||
expected_event_summaries=[
|
||||
'mars - Martian Special Interest Group',
|
||||
])
|
||||
|
||||
def test_upcoming_json(self):
|
||||
make_meeting_test_data(create_interims=True)
|
||||
|
|
|
@ -1336,6 +1336,58 @@ def ical_session_status(session_with_current_status):
|
|||
else:
|
||||
return "CONFIRMED"
|
||||
|
||||
def parse_agenda_filter_params(querydict):
|
||||
"""Parse agenda filter parameters from a request"""
|
||||
if len(querydict) == 0:
|
||||
return None
|
||||
|
||||
# Parse group filters from GET parameters. The keys in this dict define the
|
||||
# allowed querystring parameters.
|
||||
filt_params = {'show': set(), 'hide': set(), 'showtypes': set(), 'hidetypes': set()}
|
||||
|
||||
for key, value in querydict.items():
|
||||
if key not in filt_params:
|
||||
raise ValueError('Unrecognized parameter "%s"' % key)
|
||||
if value is None:
|
||||
return ValueError(
|
||||
'Parameter "%s" is not assigned a value (use "key=" for an empty value)' % key
|
||||
)
|
||||
vals = unquote(value).lower().split(',')
|
||||
filt_params[key] = set([v for v in vals if len(v) > 0]) # remove empty strings
|
||||
|
||||
return filt_params
|
||||
|
||||
|
||||
def should_include_assignment(filter_params, assignment):
|
||||
"""Decide whether to include an assignment
|
||||
|
||||
When filtering by wg, uses historic_group if available as an attribute
|
||||
on the session, otherwise falls back to using group.
|
||||
"""
|
||||
historic_group = getattr(assignment.session, 'historic_group', None)
|
||||
if historic_group:
|
||||
group_acronym = historic_group.acronym
|
||||
parent = historic_group.historic_parent
|
||||
parent_acronym = parent.acronym if parent else None
|
||||
else:
|
||||
group = assignment.session.group
|
||||
group_acronym = group.acronym
|
||||
if group.parent:
|
||||
parent_acronym = group.parent.acronym
|
||||
else:
|
||||
parent_acronym = None
|
||||
session_type = assignment.timeslot.type_id
|
||||
|
||||
# Hide if wg or type hide lists apply
|
||||
if (group_acronym in filter_params['hide']) or (session_type in filter_params['hidetypes']):
|
||||
return False
|
||||
|
||||
# Show if any of the show lists apply, including showing by parent group
|
||||
return ((group_acronym in filter_params['show']) or
|
||||
(parent_acronym in filter_params['show']) or
|
||||
(session_type in filter_params['showtypes']))
|
||||
|
||||
|
||||
def agenda_ical(request, num=None, name=None, acronym=None, session_id=None):
|
||||
"""Agenda ical view
|
||||
|
||||
|
@ -1364,46 +1416,14 @@ def agenda_ical(request, num=None, name=None, acronym=None, session_id=None):
|
|||
assignments = schedule.assignments.exclude(timeslot__type__in=['lead','offagenda'])
|
||||
assignments = preprocess_assignments_for_agenda(assignments, meeting)
|
||||
|
||||
if len(request.GET) > 0:
|
||||
# Parse group filters from GET parameters. The keys in this dict define the
|
||||
# allowed querystring parameters.
|
||||
filt_params = {'show': set(), 'hide': set(), 'showtypes': set(), 'hidetypes': set()}
|
||||
|
||||
for key, value in request.GET.items():
|
||||
if key not in filt_params:
|
||||
return HttpResponseBadRequest('Unrecognized parameter "%s"' % key)
|
||||
if value is None:
|
||||
return HttpResponseBadRequest(
|
||||
'Parameter "%s" is not assigned a value (use "key=" for an empty value)' % key
|
||||
)
|
||||
filt_params[key] = set(unquote(value).lower().split(','))
|
||||
try:
|
||||
filt_params = parse_agenda_filter_params(request.GET)
|
||||
except ValueError as e:
|
||||
return HttpResponseBadRequest(str(e))
|
||||
|
||||
def _should_include_assignment(assignment):
|
||||
"""Decide whether to include an assignment
|
||||
|
||||
Relies on filt_params from parent scope.
|
||||
"""
|
||||
historic_group = assignment.session.historic_group
|
||||
if historic_group:
|
||||
group_acronym = historic_group.acronym
|
||||
parent = historic_group.historic_parent
|
||||
parent_acronym = parent.acronym if parent else None
|
||||
else:
|
||||
group_acronym = None
|
||||
parent_acronym = None
|
||||
session_type = assignment.timeslot.type_id
|
||||
|
||||
# Hide if wg or type hide lists apply
|
||||
if (group_acronym in filt_params['hide']) or (session_type in filt_params['hidetypes']):
|
||||
return False
|
||||
|
||||
# Show if any of the show lists apply, including showing by parent group
|
||||
return ((group_acronym in filt_params['show']) or
|
||||
(parent_acronym in filt_params['show']) or
|
||||
(session_type in filt_params['showtypes']))
|
||||
|
||||
if filt_params is not None:
|
||||
# Apply the filter
|
||||
assignments = [a for a in assignments if _should_include_assignment(a)]
|
||||
assignments = [a for a in assignments if should_include_assignment(filt_params, a)]
|
||||
|
||||
if acronym:
|
||||
assignments = [ a for a in assignments if a.session.historic_group and a.session.historic_group.acronym == acronym ]
|
||||
|
@ -2754,24 +2774,47 @@ def past(request):
|
|||
})
|
||||
|
||||
def upcoming(request):
|
||||
'''List of upcoming meetings'''
|
||||
"""List of upcoming meetings
|
||||
|
||||
Only querystring filters by wg name are supported. Always includes IETF meetings;
|
||||
filters 'interim' type meetings by wg name as requested. The showtypes/hidetypes
|
||||
filters are ignored..
|
||||
"""
|
||||
today = datetime.date.today()
|
||||
filter_params = parse_agenda_filter_params(request.GET)
|
||||
|
||||
# Get ietf meetings starting 7 days ago, and interim meetings starting today
|
||||
ietf_meetings = Meeting.objects.filter(type_id='ietf', date__gte=today-datetime.timedelta(days=7))
|
||||
for m in ietf_meetings:
|
||||
m.end = m.date+datetime.timedelta(days=m.days)
|
||||
entries = list(ietf_meetings)
|
||||
|
||||
interim_sessions = add_event_info_to_session_qs(
|
||||
Session.objects.filter(
|
||||
meeting__type_id='interim',
|
||||
meeting__type_id='interim',
|
||||
timeslotassignments__schedule=F('meeting__schedule'),
|
||||
timeslotassignments__timeslot__time__gte=today
|
||||
)
|
||||
).filter(current_status__in=('sched','canceled'))
|
||||
if filter_params is not None:
|
||||
group_shown = interim_sessions.filter(
|
||||
group__acronym__in=filter_params['show']
|
||||
)
|
||||
parent_group_shown = interim_sessions.filter(
|
||||
group__parent__acronym__in=filter_params['show']
|
||||
)
|
||||
# The '|' combines querysets with OR - qs.filter(x=1) | qs.filter(y=2)
|
||||
# translates to a 'WHERE x=1 OR y=2' in the SQL.
|
||||
interim_sessions = (
|
||||
group_shown | parent_group_shown
|
||||
).exclude(
|
||||
# N.B., we only consider parent group (area) for show, not for hide.
|
||||
# This is consistent with previous behavior but is worth revisiting.
|
||||
group__acronym__in=filter_params['hide']
|
||||
)
|
||||
|
||||
for session in interim_sessions:
|
||||
session.historic_group = session.group
|
||||
|
||||
entries = list(ietf_meetings)
|
||||
entries.extend(list(interim_sessions))
|
||||
entries.sort(key = lambda o: pytz.utc.localize(datetime.datetime.combine(o.date, datetime.datetime.min.time())) if isinstance(o,Meeting) else o.official_timeslotassignment().timeslot.utc_start_time())
|
||||
|
||||
|
@ -2800,8 +2843,11 @@ def upcoming(request):
|
|||
|
||||
|
||||
def upcoming_ical(request):
|
||||
'''Return Upcoming meetings in iCalendar file'''
|
||||
filters = request.GET.getlist('filters')
|
||||
"""Return Upcoming meetings in iCalendar file
|
||||
|
||||
Filters by wg name and session type.
|
||||
"""
|
||||
filter_params = parse_agenda_filter_params(request.GET)
|
||||
today = datetime.date.today()
|
||||
|
||||
# get meetings starting 7 days ago -- we'll filter out sessions in the past further down
|
||||
|
@ -2818,13 +2864,8 @@ def upcoming_ical(request):
|
|||
).distinct())
|
||||
|
||||
# apply filters
|
||||
if filters:
|
||||
assignments = [a for a in assignments if
|
||||
a.session.group and (
|
||||
a.session.group.acronym in filters or (
|
||||
a.session.group.parent and a.session.group.parent.acronym in filters
|
||||
)
|
||||
) ]
|
||||
if filter_params is not None:
|
||||
assignments = [a for a in assignments if should_include_assignment(filter_params, a)]
|
||||
|
||||
# we already collected sessions with current_status, so reuse those
|
||||
sessions = {s.pk: s for m in meetings for s in m.sessions}
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
<div class="panel panel-default" id="futuremeets">
|
||||
<div class="panel-heading">
|
||||
Future Meetings
|
||||
<a class="regular pull-right" title="icalendar entry for all scheduled future {{group.acronym}} meetings" href="{% url 'ietf.meeting.views.upcoming_ical' %}?filters={{group.acronym}}"><span class="fa fa-calendar"></span></a>
|
||||
<a class="regular pull-right" title="icalendar entry for all scheduled future {{group.acronym}} meetings" href="{% url 'ietf.meeting.views.upcoming_ical' %}?show={{group.acronym}}"><span class="fa fa-calendar"></span></a>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% with sessions=future show_request=True show_ical=True can_edit_materials=can_edit %}
|
||||
|
|
|
@ -126,6 +126,7 @@
|
|||
<div class="btn-group"><button class="btn btn-default pickview ietf"> IETF</button></div>
|
||||
<div class="btn-group"><button class="btn btn-default pickview iesg"> IESG</button></div>
|
||||
<div class="btn-group"><button class="btn btn-default pickview iab"> IAB</button></div>
|
||||
<div class="btn-group"><button class="btn btn-default pickview secretariat"> Secretariat</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -192,7 +193,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if item.timeslot.type.slug == 'break' or item.timeslot.type.slug == 'reg' or item.timeslot.type.slug == 'other' %}
|
||||
<tr id="row-{{ item.slug }}" timeslot-type="{{item.timeslot.type.slug}}">
|
||||
<tr id="row-{{ item.slug }}" data-item-group={{ item.session.group.acronym }} data-timeslot-type="{{item.timeslot.type.slug}}">
|
||||
<td class="text-nowrap text-right">
|
||||
{% if "-utc" in request.path %}
|
||||
{{item.timeslot.utc_start_time|date:"G:i"}}-{{item.timeslot.utc_end_time|date:"G:i"}}
|
||||
|
@ -250,7 +251,7 @@
|
|||
|
||||
{% if item.timeslot.type_id == 'regular' or item.timeslot.type.slug == 'plenary' %}
|
||||
{% if item.session.historic_group %}
|
||||
<tr id="row-{{item.slug}}" timeslot-type="{{item.timeslot.type.slug}}" data-ske="row-{{ item.slug }}" {% if item.timeslot.type.slug == 'plenary' %}class="{{item.timeslot.type.slug}}danger"{% endif %}>
|
||||
<tr id="row-{{item.slug}}" data-item-group={{ item.session.group.acronym }} data-timeslot-type="{{item.timeslot.type.slug}}" data-ske="row-{{ item.slug }}" {% if item.timeslot.type.slug == 'plenary' %}class="{{item.timeslot.type.slug}}danger"{% endif %}>
|
||||
{% if item.timeslot.type.slug == 'plenary' %}
|
||||
<th class="text-nowrap text-right">
|
||||
{% if "-utc" in request.path %}
|
||||
|
@ -361,7 +362,7 @@
|
|||
<script>
|
||||
function parse_query_params(qs) {
|
||||
var params = {};
|
||||
qs = qs.replace(/^\?/, '');
|
||||
qs = qs.replace(/^\?/, '').toLowerCase();
|
||||
if (qs) {
|
||||
var param_strs = qs.split('&');
|
||||
for (var ii = 0; ii < param_strs.length; ii++) {
|
||||
|
@ -405,23 +406,23 @@
|
|||
// loop through the has items and change the UI element and row visibilities accordingly
|
||||
$.each(filter_params['show_groups'], function (i, v) {
|
||||
// this is a regular item by wg: when present, show these rows
|
||||
$('[id^="row-"]').filter('[id*="-' + v + '"]').show();
|
||||
$('[id^="row-"]').filter('[data-item-group="'+ v +'"]').show();
|
||||
$(".view." + v).find("button").addClass("active disabled");
|
||||
$("button.pickview." + v).addClass("active");
|
||||
});
|
||||
$.each(filter_params['show_types'], function (i, v) {
|
||||
// this is a regular item by type: when present, show these rows
|
||||
$('[id^="row-"]').filter('[timeslot-type*="' + v + '"]').show();
|
||||
$('[id^="row-"]').filter('[data-timeslot-type*="' + v + '"]').show();
|
||||
});
|
||||
$.each(filter_params['hide_groups'], function (i, v) {
|
||||
// this is a "negative" item by wg: when present, hide these rows
|
||||
$('[id^="row-"]').filter('[id*="-' + v + '"]').hide();
|
||||
$('[id^="row-"]').filter('[data-item-group="'+ v +'"]').hide();
|
||||
$(".view." + v).find("button").removeClass("active disabled");
|
||||
$("button.pickviewneg." + v).removeClass("active");
|
||||
});
|
||||
$.each(filter_params['hide_types'], function (i, v) {
|
||||
// this is a "negative" item by type: when present, hide these rows
|
||||
$('[id^="row-"]').filter('[timeslot-type*="' + v + '"]').hide();
|
||||
$('[id^="row-"]').filter('[data-timeslot-type*="' + v + '"]').hide();
|
||||
});
|
||||
|
||||
// show the week view
|
||||
|
|
Loading…
Reference in a new issue