Skip to content

Commit

Permalink
fix: Render complete iCalendar event when show_location is False (#5394)
Browse files Browse the repository at this point in the history
* test: More carefully test event syntax in ical file

* test: Test agenda.ics view with all meeting sessions

* fix: Render complete iCalendar event when show_location is False

* chore: Fix confusing comment
  • Loading branch information
jennifer-richards committed Mar 26, 2023
1 parent f40681d commit bc9c74e
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 13 deletions.
15 changes: 12 additions & 3 deletions ietf/meeting/tests_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ def test_meeting_agenda(self):
slot = TimeSlot.objects.get(sessionassignments__session=session,sessionassignments__schedule=meeting.schedule)
slot.location.urlresource_set.create(name_id='meetecho_onsite', url='https://onsite.example.com')
slot.location.urlresource_set.create(name_id='meetecho', url='https://meetecho.example.com')
meeting.timeslot_set.filter(type_id="break").update(show_location=False)
#
self.write_materials_files(meeting, session)
#
Expand Down Expand Up @@ -348,9 +349,17 @@ def test_meeting_agenda(self):
self.assertContains(r, session.materials.filter(type='slides').exclude(states__type__slug='slides',states__slug='deleted').first().uploaded_filename)
self.assertNotContains(r, session.materials.filter(type='slides',states__type__slug='slides',states__slug='deleted').first().uploaded_filename)

# iCal
r = self.client.get(urlreverse("ietf.meeting.views.agenda_ical", kwargs=dict(num=meeting.number))
+ "?show=" + session.group.parent.acronym.upper())
# iCal, no session filtering
ical_url = urlreverse("ietf.meeting.views.agenda_ical", kwargs=dict(num=meeting.number))
r = self.client.get(ical_url)
with open('./ical-output.ics', 'w') as f:
f.write(r.content.decode())
assert_ical_response_is_valid(self, r)
self.assertContains(r, "BEGIN:VTIMEZONE")
self.assertContains(r, "END:VTIMEZONE")

# iCal, single group
r = self.client.get(ical_url + "?show=" + session.group.parent.acronym.upper())
assert_ical_response_is_valid(self, r)
self.assertContains(r, session.group.acronym)
self.assertContains(r, session.group.name)
Expand Down
4 changes: 2 additions & 2 deletions ietf/templates/meeting/agenda.ics
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ PRODID:-//IETF//datatracker.ietf.org ical agenda//EN
UID:ietf-{{schedule.meeting.number}}-{{item.timeslot.pk}}-{{item.session.group.acronym}}
SUMMARY:{% if item.session.name %}{{item.session.name|ics_esc}}{% else %}{{item.session.group_at_the_time.acronym|lower}} - {{item.session.group_at_the_time.name}}{%endif%}{% if item.session.agenda_note %} ({{item.session.agenda_note}}){% endif %}
{% if item.timeslot.show_location %}LOCATION:{{item.timeslot.get_location}}
STATUS:{{item.session.ical_status}}
{% endif %}STATUS:{{item.session.ical_status}}
CLASS:PUBLIC
DTSTART{% ics_date_time item.timeslot.local_start_time schedule.meeting.time_zone %}
DTEND{% ics_date_time item.timeslot.local_end_time schedule.meeting.time_zone %}
Expand All @@ -29,4 +29,4 @@ DESCRIPTION:{{item.timeslot.name|ics_esc}}\n{% if item.session.agenda_note %}
\n{# link agenda for ietf meetings #}
See in schedule: {% absurl 'agenda' num=schedule.meeting.number %}#row-{{ item.slug }}\n{% endif %}
END:VEVENT
{% endif %}{% endfor %}END:VCALENDAR{% endcache %}{% endtimezone %}{% endautoescape %}
{% endfor %}END:VCALENDAR{% endcache %}{% endtimezone %}{% endautoescape %}
41 changes: 33 additions & 8 deletions ietf/utils/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def assert_ical_response_is_valid(test_inst, response, expected_event_summaries=
expected_event_uids=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. Checks that
Based on RFC5545, but not exhaustive by any means. Assumes a single iCalendar object. Checks that
expected_event_summaries/_uids are found, but other events are allowed to be present. Specify the
expected_event_count if you want to reject additional events. If any of these are None,
the check for that property is skipped.
Expand All @@ -132,18 +132,43 @@ def assert_ical_response_is_valid(test_inst, response, expected_event_summaries=
test_inst.assertContains(response, 'VERSION', count=1)

# Validate event objects
event_count = 0
uids_found = set()
summaries_found = set()
got_begin = False
cur_event_props = set()
for line_num, line in enumerate(response.content.decode().split("\n")):
line = line.rstrip()
if line == 'BEGIN:VEVENT':
test_inst.assertFalse(got_begin, f"Nested BEGIN:VEVENT found on line {line_num + 1}")
got_begin = True
elif line == 'END:VEVENT':
test_inst.assertTrue(got_begin, f"Unexpected END:VEVENT on line {line_num + 1}")
test_inst.assertIn("uid", cur_event_props, f"Found END:VEVENT without UID on line {line_num + 1}")
got_begin = False
cur_event_props.clear()
event_count += 1
elif got_begin:
# properties in an event
if line.startswith("UID:"):
# mandatory, not more than once
test_inst.assertNotIn("uid", cur_event_props, f"Two UID properties in single event on line {line_num + 1}")
cur_event_props.add("uid")
uids_found.add(line.split(":", 1)[1])
elif line.startswith("SUMMARY:"):
# optional, not more than once
test_inst.assertNotIn("summary", cur_event_props, f"Two SUMMARY properties in single event on line {line_num + 1}")
cur_event_props.add("summary")
summaries_found.add(line.split(":", 1)[1])

if expected_event_summaries is not None:
for summary in expected_event_summaries:
test_inst.assertContains(response, 'SUMMARY:' + summary)
test_inst.assertCountEqual(summaries_found, set(expected_event_summaries))

if expected_event_uids is not None:
for uid in expected_event_uids:
test_inst.assertContains(response, 'UID:' + uid)
test_inst.assertCountEqual(uids_found, set(expected_event_uids))

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)
test_inst.assertEqual(event_count, expected_event_count)

# make sure no doubled colons after timestamp properties
test_inst.assertNotContains(response, 'DTSTART::')
Expand Down

0 comments on commit bc9c74e

Please sign in to comment.