From 7f62d7f80626974a7873412166609c1392f786eb Mon Sep 17 00:00:00 2001 From: Sangho Na Date: Sun, 21 Jul 2024 11:15:35 +1200 Subject: [PATCH 1/5] test: Update tests to check for Updated field in agenda.txt --- ietf/meeting/tests_views.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index d783ed9c75..a968374ad1 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,8 @@ 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}") # future meeting, no agenda r = self.client.get(urlreverse("ietf.meeting.views.agenda_plain", kwargs=dict(num=future_meeting.number, ext=".txt"))) @@ -332,6 +336,7 @@ def test_meeting_agenda(self): self.assertContains(r, session.materials.get(type='agenda').uploaded_filename) 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) + self.assertNotContains(r, "Updated ") # Updated not used in CSV version # CSV, utc r = self.client.get(urlreverse( @@ -353,6 +358,7 @@ def test_meeting_agenda(self): self.assertContains(r, session.materials.get(type='agenda').uploaded_filename) 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) + self.assertNotContains(r, "Updated ") # Updated not used in CSV version # iCal, no session filtering ical_url = urlreverse("ietf.meeting.views.agenda_ical", kwargs=dict(num=meeting.number)) From e57a0a94e4fe9f5c0e9467e78fb0dbba94d585fa Mon Sep 17 00:00:00 2001 From: Sangho Na Date: Sun, 21 Jul 2024 12:39:09 +1200 Subject: [PATCH 2/5] fix: Hide Updated in agenda.txt if too old --- ietf/meeting/tests_views.py | 33 +++++++++++++++++++++++++++++++ ietf/templates/meeting/agenda.txt | 2 ++ 2 files changed, 35 insertions(+) diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index a968374ad1..962defc9c8 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -314,6 +314,39 @@ def test_meeting_agenda(self): updated = meeting.updated().astimezone(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z") self.assertContains(r, f"Updated {updated}") + # text, invalid updated (1) + with patch( + "ietf.meeting.models.Meeting.updated", + return_value=pytz.utc.localize(datetime.datetime(1970, 1, 1, 0, 0, 0)), + ): + r = self.client.get(urlreverse( + "ietf.meeting.views.agenda_plain", + kwargs=dict(num=meeting.number, ext=".txt", utc="-utc"), + )) + self.assertNotContains(r, "Updated ") + + # text, invalid updated (2) + with patch( + "ietf.meeting.models.Meeting.updated", + return_value=pytz.utc.localize(datetime.datetime(1979, 12, 31, 23, 59, 59)), + ): + r = self.client.get(urlreverse( + "ietf.meeting.views.agenda_plain", + kwargs=dict(num=meeting.number, ext=".txt", utc="-utc"), + )) + self.assertNotContains(r, "Updated ") + + # text, valid updated + with patch( + "ietf.meeting.models.Meeting.updated", + return_value=pytz.utc.localize(datetime.datetime(1980, 1, 1, 0, 0, 0)), + ): + r = self.client.get(urlreverse( + "ietf.meeting.views.agenda_plain", + kwargs=dict(num=meeting.number, ext=".txt", utc="-utc"), + )) + self.assertContains(r, "Updated 1980-01-01 00:00:00 UTC") + # future meeting, no agenda r = self.client.get(urlreverse("ietf.meeting.views.agenda_plain", kwargs=dict(num=future_meeting.number, ext=".txt"))) self.assertContains(r, "There is no agenda available yet.") diff --git a/ietf/templates/meeting/agenda.txt b/ietf/templates/meeting/agenda.txt index d00be80d14..6fafb96988 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|date:"Y-m-d" >= "1980-01-01" %} {% 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 %} From 057a8008da4be13e0758c5c1a173dbb0150040f8 Mon Sep 17 00:00:00 2001 From: Sangho Na Date: Fri, 26 Jul 2024 09:38:38 +1200 Subject: [PATCH 3/5] test: Remove confusing tests on CSV agenda --- ietf/meeting/tests_views.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index 962defc9c8..299497266f 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -369,7 +369,6 @@ def test_meeting_agenda(self): self.assertContains(r, session.materials.get(type='agenda').uploaded_filename) 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) - self.assertNotContains(r, "Updated ") # Updated not used in CSV version # CSV, utc r = self.client.get(urlreverse( @@ -391,7 +390,6 @@ def test_meeting_agenda(self): self.assertContains(r, session.materials.get(type='agenda').uploaded_filename) 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) - self.assertNotContains(r, "Updated ") # Updated not used in CSV version # iCal, no session filtering ical_url = urlreverse("ietf.meeting.views.agenda_ical", kwargs=dict(num=meeting.number)) From e71be3ee320a91e70063874af54480bba8c3288d Mon Sep 17 00:00:00 2001 From: Sangho Na Date: Fri, 26 Jul 2024 19:39:41 +1200 Subject: [PATCH 4/5] refactor: Make updated() return None when no valid timestamp found --- client/agenda/Agenda.vue | 2 +- ietf/meeting/models.py | 14 ++++--- ietf/meeting/tests_views.py | 67 ++++++++++++++++++------------- ietf/meeting/utils.py | 3 +- ietf/templates/meeting/agenda.txt | 2 +- 5 files changed, 51 insertions(+), 37 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..e5af0a7018 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -369,13 +369,15 @@ 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] + if valid_only := [dt for dt in dts if dt is not None]: + return max(valid_only) + return None @memoize def previous_meeting(self): diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index 299497266f..0c01a7b52e 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -314,39 +314,14 @@ def test_meeting_agenda(self): updated = meeting.updated().astimezone(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z") self.assertContains(r, f"Updated {updated}") - # text, invalid updated (1) - with patch( - "ietf.meeting.models.Meeting.updated", - return_value=pytz.utc.localize(datetime.datetime(1970, 1, 1, 0, 0, 0)), - ): - r = self.client.get(urlreverse( - "ietf.meeting.views.agenda_plain", - kwargs=dict(num=meeting.number, ext=".txt", utc="-utc"), - )) - self.assertNotContains(r, "Updated ") - - # text, invalid updated (2) - with patch( - "ietf.meeting.models.Meeting.updated", - return_value=pytz.utc.localize(datetime.datetime(1979, 12, 31, 23, 59, 59)), - ): + # 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 ") - # text, valid updated - with patch( - "ietf.meeting.models.Meeting.updated", - return_value=pytz.utc.localize(datetime.datetime(1980, 1, 1, 0, 0, 0)), - ): - r = self.client.get(urlreverse( - "ietf.meeting.views.agenda_plain", - kwargs=dict(num=meeting.number, ext=".txt", utc="-utc"), - )) - self.assertContains(r, "Updated 1980-01-01 00:00:00 UTC") - # future meeting, no agenda r = self.client.get(urlreverse("ietf.meeting.views.agenda_plain", kwargs=dict(num=future_meeting.number, ext=".txt"))) self.assertContains(r, "There is no agenda available yet.") @@ -896,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() @@ -4989,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") @@ -5011,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 6fafb96988..7a49dde0c8 100644 --- a/ietf/templates/meeting/agenda.txt +++ b/ietf/templates/meeting/agenda.txt @@ -7,7 +7,7 @@ {% 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|date:"Y-m-d" >= "1980-01-01" %} +{% if updated %} {% filter center:72 %}Updated {{ updated|date:"Y-m-d H:i:s T" }}{% endfilter %} {% endif %} From f12112703446e5057751115d6dbb948f5af7fa6d Mon Sep 17 00:00:00 2001 From: Sangho Na Date: Sat, 27 Jul 2024 07:27:44 +1200 Subject: [PATCH 5/5] refactor: Remove walrus operator --- ietf/meeting/models.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index e5af0a7018..fa1ad9d674 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -375,9 +375,8 @@ def updated(self): 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"] dts = [timeslots_updated, sessions_updated, assignments_updated] - if valid_only := [dt for dt in dts if dt is not None]: - return max(valid_only) - return None + 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):