Skip to content

Commit 7006770

Browse files
authored
refactor: use ical for upcoming meetings (#9845)
* refactor: use ical for upcoming meetings * fix: unfold to pass tests * fix: make material title, type and url on one line
1 parent 9244e61 commit 7006770

File tree

3 files changed

+64
-44
lines changed

3 files changed

+64
-44
lines changed

ietf/meeting/tests_views.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5302,15 +5302,18 @@ def test_upcoming_ical(self):
53025302
assert_ical_response_is_valid(self, r,
53035303
expected_event_summaries=expected_event_summaries,
53045304
expected_event_count=len(expected_event_summaries))
5305-
self.assertContains(r, 'Remote instructions: https://someurl.example.com')
5305+
# Unfold long lines that might have been folded by iCal
5306+
content_unfolded = r.content.decode('utf-8').replace('\r\n ', '')
5307+
self.assertIn('Remote instructions: https://someurl.example.com', content_unfolded)
53065308

53075309
Session.objects.filter(meeting__type_id='interim').update(remote_instructions='')
53085310
r = self.client.get(url)
53095311
self.assertEqual(r.status_code, 200)
53105312
assert_ical_response_is_valid(self, r,
53115313
expected_event_summaries=expected_event_summaries,
53125314
expected_event_count=len(expected_event_summaries))
5313-
self.assertNotContains(r, 'Remote instructions:')
5315+
content_unfolded = r.content.decode('utf-8').replace('\r\n ', '')
5316+
self.assertNotIn('Remote instructions:', content_unfolded)
53145317

53155318
updated = meeting.updated()
53165319
self.assertIsNotNone(updated)

ietf/meeting/views.py

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4430,16 +4430,7 @@ def upcoming_ical(request):
44304430
else:
44314431
ietfs = []
44324432

4433-
meeting_vtz = {meeting.vtimezone() for meeting in meetings}
4434-
meeting_vtz.discard(None)
4435-
4436-
# icalendar response file should have '\r\n' line endings per RFC5545
4437-
response = render_to_string('meeting/upcoming.ics', {
4438-
'vtimezones': ''.join(sorted(meeting_vtz)),
4439-
'assignments': assignments,
4440-
'ietfs': ietfs,
4441-
}, request=request)
4442-
response = parse_ical_line_endings(response)
4433+
response = render_upcoming_ical(assignments, ietfs, request)
44434434

44444435
response = HttpResponse(response, content_type='text/calendar')
44454436
response['Content-Disposition'] = 'attachment; filename="upcoming.ics"'
@@ -4513,6 +4504,64 @@ def render_important_dates_ical(meetings, request):
45134504

45144505
return cal.to_ical().decode("utf-8")
45154506

4507+
def render_upcoming_ical(assignments, meetings, request):
4508+
"""Generate upcoming using the icalendar library"""
4509+
4510+
cal = Calendar()
4511+
cal.add("prodid", "-//IETF//datatracker.ietf.org ical upcoming//EN")
4512+
cal.add("version", "2.0")
4513+
cal.add("method", "PUBLISH")
4514+
4515+
for item in assignments:
4516+
event = Event()
4517+
4518+
event.add("uid", f"ietf-{item.session.meeting.number}-{item.timeslot.pk}")
4519+
event.add("summary", f"{item.session.group.acronym.lower()} - {item.session.name if item.session.name else item.session.group.name}")
4520+
4521+
if item.schedule.meeting.city:
4522+
event.add("location", f"{item.schedule.meeting.city},{item.schedule.meeting.country}")
4523+
4524+
event.add("status", item.session.ical_status)
4525+
event.add("class", "PUBLIC")
4526+
4527+
event.add("dtstart", item.timeslot.utc_start_time())
4528+
event.add("dtend", item.timeslot.utc_end_time())
4529+
event.add("dtstamp", item.timeslot.modified)
4530+
if item.session.agenda():
4531+
event.add("url", item.session.agenda().get_href())
4532+
4533+
description_lines = []
4534+
if item.timeslot.name:
4535+
description_lines.append(f"{item.timeslot.name}")
4536+
if item.session.agenda_note:
4537+
description_lines.append(f"Note: {item.session.agenda_note}")
4538+
4539+
for material in item.session.materials.all():
4540+
title_part = f" ({material.title})" if material.type.name != "Agenda" else ""
4541+
description_lines.append(f"{material.type}{title_part}: {material.get_href()}")
4542+
4543+
if item.session.remote_instructions:
4544+
description_lines.append(f"Remote instructions: {item.session.remote_instructions}")
4545+
4546+
event.add("description", "\n".join(description_lines))
4547+
cal.add_component(event)
4548+
4549+
for meeting in meetings:
4550+
event = Event()
4551+
event.add("uid", f"ietf-{meeting.number}")
4552+
event.add("summary", f"IETF {meeting.number}")
4553+
if meeting.city:
4554+
event.add("location", f"{meeting.city},{meeting.country}")
4555+
event.add("class", "PUBLIC")
4556+
event.add("dtstart", meeting.date)
4557+
event.add("dtend", meeting.end_date() + datetime.timedelta(days=1))
4558+
event.add("dtstamp", meeting.cached_updated)
4559+
event.add("url", f"{request.scheme}://{request.get_host()}{reverse('agenda', kwargs={'num': meeting.number})}")
4560+
4561+
cal.add_component(event)
4562+
4563+
return cal.to_ical().decode("utf-8")
4564+
45164565
def upcoming_json(request):
45174566
'''Return Upcoming meetings in json format'''
45184567
today = date_today()

ietf/templates/meeting/upcoming.ics

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)