From b90820eb7a74ecaa417df120229ca3f798602e69 Mon Sep 17 00:00:00 2001 From: Sangho Na Date: Thu, 8 Aug 2024 02:15:01 +1200 Subject: [PATCH] fix: Hide last modified field in agenda when unavailable (#7722) * test: Update tests to check for Updated field in agenda.txt * fix: Hide Updated in agenda.txt if too old * test: Remove confusing tests on CSV agenda * refactor: Make updated() return None when no valid timestamp found * refactor: Remove walrus operator --- client/agenda/Agenda.vue | 2 +- ietf/meeting/models.py | 13 ++++---- ietf/meeting/tests_views.py | 50 ++++++++++++++++++++++++++++++- ietf/meeting/utils.py | 3 +- ietf/templates/meeting/agenda.txt | 2 ++ 5 files changed, 61 insertions(+), 9 deletions(-) diff --git a/client/agenda/Agenda.vue b/client/agenda/Agenda.vue index 34a3751f23..40dae008a4 100644 --- a/client/agenda/Agenda.vue +++ b/client/agenda/Agenda.vue @@ -323,7 +323,7 @@ const meetingUpdated = computed(() => { if (!agendaStore.meeting.updated) { return false } const updatedDatetime = DateTime.fromISO(agendaStore.meeting.updated).setZone(agendaStore.timezone) - if (!updatedDatetime.isValid || updatedDatetime < DateTime.fromISO('1980-01-01')) { + if (!updatedDatetime.isValid) { return false } diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index dd6e2db6c5..fa1ad9d674 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -369,13 +369,14 @@ def vtimezone(self): def updated(self): # should be Meeting.modified, but we don't have that - min_time = pytz.utc.localize(datetime.datetime(1970, 1, 1, 0, 0, 0)) - timeslots_updated = self.timeslot_set.aggregate(Max('modified'))["modified__max"] or min_time - sessions_updated = self.session_set.aggregate(Max('modified'))["modified__max"] or min_time - assignments_updated = min_time + timeslots_updated = self.timeslot_set.aggregate(Max('modified'))["modified__max"] + sessions_updated = self.session_set.aggregate(Max('modified'))["modified__max"] + assignments_updated = None if self.schedule: - assignments_updated = SchedTimeSessAssignment.objects.filter(schedule__in=[self.schedule, self.schedule.base if self.schedule else None]).aggregate(Max('modified'))["modified__max"] or min_time - return max(timeslots_updated, sessions_updated, assignments_updated) + assignments_updated = SchedTimeSessAssignment.objects.filter(schedule__in=[self.schedule, self.schedule.base if self.schedule else None]).aggregate(Max('modified'))["modified__max"] + dts = [timeslots_updated, sessions_updated, assignments_updated] + valid_only = [dt for dt in dts if dt is not None] + return max(valid_only) if valid_only else None @memoize def previous_meeting(self): diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index da82afb329..60eef96b2d 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -294,6 +294,8 @@ def test_meeting_agenda(self): (slot.time + slot.duration).astimezone(meeting.tz()).strftime("%H%M"), )) self.assertContains(r, f"shown in the {meeting.tz()} time zone") + updated = meeting.updated().astimezone(meeting.tz()).strftime("%Y-%m-%d %H:%M:%S %Z") + self.assertContains(r, f"Updated {updated}") # text, UTC r = self.client.get(urlreverse( @@ -309,6 +311,16 @@ def test_meeting_agenda(self): (slot.time + slot.duration).astimezone(datetime.timezone.utc).strftime("%H%M"), )) self.assertContains(r, "shown in UTC") + updated = meeting.updated().astimezone(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z") + self.assertContains(r, f"Updated {updated}") + + # text, invalid updated (none) + with patch("ietf.meeting.models.Meeting.updated", return_value=None): + r = self.client.get(urlreverse( + "ietf.meeting.views.agenda_plain", + kwargs=dict(num=meeting.number, ext=".txt", utc="-utc"), + )) + self.assertNotContains(r, "Updated ") # future meeting, no agenda r = self.client.get(urlreverse("ietf.meeting.views.agenda_plain", kwargs=dict(num=future_meeting.number, ext=".txt"))) @@ -859,6 +871,24 @@ def test_important_dates_ical(self): for d in meeting.importantdate_set.all(): self.assertContains(r, d.date.isoformat()) + updated = meeting.updated() + self.assertIsNotNone(updated) + expected_updated = updated.astimezone(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ") + self.assertContains(r, f"DTSTAMP:{expected_updated}") + dtstamps_count = r.content.decode("utf-8").count(f"DTSTAMP:{expected_updated}") + self.assertEqual(dtstamps_count, meeting.importantdate_set.count()) + + # With default cached_updated, 1970-01-01 + with patch("ietf.meeting.models.Meeting.updated", return_value=None): + r = self.client.get(url) + for d in meeting.importantdate_set.all(): + self.assertContains(r, d.date.isoformat()) + + expected_updated = "19700101T000000Z" + self.assertContains(r, f"DTSTAMP:{expected_updated}") + dtstamps_count = r.content.decode("utf-8").count(f"DTSTAMP:{expected_updated}") + self.assertEqual(dtstamps_count, meeting.importantdate_set.count()) + def test_group_ical(self): meeting = make_meeting_test_data() s1 = Session.objects.filter(meeting=meeting, group__acronym="mars").first() @@ -4952,7 +4982,23 @@ def test_upcoming_ical(self): expected_event_count=len(expected_event_summaries)) self.assertNotContains(r, 'Remote instructions:') - def test_upcoming_ical_filter(self): + updated = meeting.updated() + self.assertIsNotNone(updated) + expected_updated = updated.astimezone(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ") + self.assertContains(r, f"DTSTAMP:{expected_updated}") + + # With default cached_updated, 1970-01-01 + with patch("ietf.meeting.models.Meeting.updated", return_value=None): + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + + self.assertEqual(meeting.type_id, "ietf") + + expected_updated = "19700101T000000Z" + self.assertEqual(1, r.content.decode("utf-8").count(f"DTSTAMP:{expected_updated}")) + + @patch("ietf.meeting.utils.preprocess_meeting_important_dates") + def test_upcoming_ical_filter(self, mock_preprocess_meeting_important_dates): # Just a quick check of functionality - details tested by test_js.InterimTests make_meeting_test_data(create_interims=True) url = urlreverse("ietf.meeting.views.upcoming_ical") @@ -4974,6 +5020,8 @@ def test_upcoming_ical_filter(self): ], expected_event_count=2) + # Verify preprocess_meeting_important_dates isn't being called + mock_preprocess_meeting_important_dates.assert_not_called() def test_upcoming_json(self): make_meeting_test_data(create_interims=True) diff --git a/ietf/meeting/utils.py b/ietf/meeting/utils.py index a60d3b010a..470795811d 100644 --- a/ietf/meeting/utils.py +++ b/ietf/meeting/utils.py @@ -609,7 +609,8 @@ def bulk_create_timeslots(meeting, times, locations, other_props): def preprocess_meeting_important_dates(meetings): for m in meetings: - m.cached_updated = m.updated() + # cached_updated must be present, set it to 1970-01-01 if necessary + m.cached_updated = m.updated() or pytz.utc.localize(datetime.datetime(1970, 1, 1, 0, 0, 0)) m.important_dates = m.importantdate_set.prefetch_related("name") for d in m.important_dates: d.midnight_cutoff = "UTC 23:59" in d.name.name diff --git a/ietf/templates/meeting/agenda.txt b/ietf/templates/meeting/agenda.txt index d00be80d14..7a49dde0c8 100644 --- a/ietf/templates/meeting/agenda.txt +++ b/ietf/templates/meeting/agenda.txt @@ -7,7 +7,9 @@ {% filter center:72 %}{{ schedule.meeting.agenda_info_note|striptags|wordwrap:72|safe }}{% endfilter %} {% endif %} {% filter center:72 %}{{ schedule.meeting.date|date:"F j" }}-{% if schedule.meeting.date.month != schedule.meeting.end_date.month %}{{ schedule.meeting.end_date|date:"F " }}{% endif %}{{ schedule.meeting.end_date|date:"j, Y" }}{% endfilter %} +{% if updated %} {% filter center:72 %}Updated {{ updated|date:"Y-m-d H:i:s T" }}{% endfilter %} +{% endif %} {% filter center:72 %}IETF agendas are subject to change, up to and during the meeting.{% endfilter %} {% filter center:72 %}Times are shown in {% if display_timezone.lower == "utc" %}UTC{% else %}the {{ display_timezone }} time zone{% endif %}.{% endfilter %}