fix: Hide last modified field in agenda when unavailable#7722
fix: Hide last modified field in agenda when unavailable#7722rjsparks merged 5 commits intoietf-tools:mainfrom microamp:fix/agenda-updated-19700101
Conversation
We may choose to implement the filter in the backend code to ensure consistency across all clients in the future. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #7722 +/- ##
==========================================
+ Coverage 88.79% 88.80% +0.01%
==========================================
Files 296 296
Lines 41319 41330 +11
==========================================
+ Hits 36688 36703 +15
+ Misses 4631 4627 -4 ☔ View full report in Codecov by Sentry. |
| (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") |
There was a problem hiding this comment.
Please double check for me that this isn't prone to race condition. Same with line 314 below. Otherwise, I'm happy to patch it with a mock return value instead of directly calling meeting.updated().
P.S. I encountered some errors locally earlier, but I haven't been able to reproduce them since, which made me think that it might be related to race condition.
There was a problem hiding this comment.
the tests all run in a single thread in a single process for now - you can't be fighting a race.
It would be nice to be able to use threading, but we have dependencies that would have to change (and we would have to rewrite our custom harness around the django test framework.)
There was a problem hiding this comment.
Thanks. Maybe it was a bug I accidentally introduced while updating the code locally. It doesn't appear to be there anymore.
Faster tests would be nice in the future.
| {% 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" %} |
There was a problem hiding this comment.
You're creating a y10k bug here, aren't you?
Unfortunately, the only way I can see to do this in the template is updated.timestamp >= 315532800 which is a pretty awful magic number (it's the number of seconds between 1970-01-01T00:00:00Z and 1980-01-01T00:00:00Z).
I wonder if we can move the check into the view and avoid passing bad data to the template / javascript
There was a problem hiding this comment.
Thanks for having a look.
You're creating a y10k bug here, aren't you?
I'm not sure. But updated will always be a datetime object, and the year must be four digits (based on the C standard). https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
Unfortunately, the only way I can see to do this in the template is updated.timestamp >= 315532800 which is a pretty awful magic number (it's the number of seconds between 1970-01-01T00:00:00Z and 1980-01-01T00:00:00Z).
I wonder how 1980-01-01 was selected as the cutover date. The default value ietf.meeting.models.Meeting.updated uses is 1970-01-01. If that was the edge case to cater for, updated.timestamp > 0 would've been sufficient, and more readable. I may be missing some context here though.
I wonder if we can move the check into the view and avoid passing bad data to the template / javascript
I can return (typo) "date": null"updated": null back if the date is too old (i.e. pre-1980), and both the JS client and the template can check for null. But this means that the behaviour of the API will change.
There was a problem hiding this comment.
You're creating a y10k bug here, aren't you?
I'm not sure. But
updatedwill always be adatetimeobject, and the year must be four digits (based on the C standard). https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
Yeah - in the year 10000, four digits is no longer adequate so it'll roll over and the check will fail
>>> "10000-01-01" < "1980-01-01"
True(Obviously it's not very likely this code will still be running in ~ 8000 years, so this is pretty pedantic)
Unfortunately, the only way I can see to do this in the template is updated.timestamp >= 315532800 which is a pretty awful magic number (it's the number of seconds between 1970-01-01T00:00:00Z and 1980-01-01T00:00:00Z).
I wonder how 1980-01-01 was selected as the cutover date. The default value
ietf.meeting.models.Meeting.updateduses is1970-01-01. If that was the edge case to cater for,updated.timestamp > 0would've been sufficient, and more readable. I may be missing some context here though.
I suspect it's just an arbitrary date that's long enough ago that we don't care about the distinction and isn't right up against the Unix epoch start so that time zone handling errors won't crop up.
I wonder if we can move the check into the view and avoid passing bad data to the template / javascript
I can return
(typo)"date": null"updated": nullback if the date is too old (i.e. pre-1980), and both the JS client and the template can check fornull. But this means that the behaviour of the API will change.
The API exists only to support the agenda, so as long as we update that we're ok.
The Meeting.updated() method is also used to set m.cached_updated that's used in a couple of templates - those will also need a tweak to handle a None value..
Discussed with Robert and the only real concern is that we end up with a method that returns Optional[datetime.datetime], which is a more complicated signature. I think that's ok, because it reflects that this method really may not have a valid response. Better to use None than to use a magic timestamp.
There was a problem hiding this comment.
Thanks for the explanation.
What I meant to say is that the edge case wouldn't have been possible, as the year cannot be more than four digits.
datetime(10000, 1, 1)Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: year 10000 is out of range
datetime(9999, 12, 31) + timedelta(days=1)Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: date value out of range
Re: Meeting.updated(), I'll update it as per the discussion above, and update the callers of the method accordingly. Thanks for pointing out that m.cached_updated also needs attention!
There was a problem hiding this comment.
I took a closer look at what m.cached_updated is being used for - it's for the DTSTAMP property in the .ics files. e.g. https://github.com/ietf-tools/datatracker/blob/main/ietf/templates/meeting/important_dates_for_meeting.ics#L6
According to RFC 5545, the DTSTAMP property, https://www.rfc-editor.org/rfc/rfc5545#section-3.8.7.2, is required.
eventprop = *(
;
; The following are REQUIRED,
; but MUST NOT occur more than once.
;
dtstamp / uid /
; ...
)
https://www.rfc-editor.org/rfc/rfc5545#section-3.6.1
So, unlike in agenda.txt, we wouldn't be able to hide the value if ietf.meeting.models.Meeting.updated returned None. Let me know if I'm reading it wrong, or if there's a later RFC that says otherwise. (I couldn't find any.)
It's probably better to leave ietf.meeting.models.Meeting.updated as-is. Let me know what you think.
There was a problem hiding this comment.
Wow, I'm disappointed to learn that datetime is limited that way.
There was a problem hiding this comment.
I still think we should make the Meeting.updated() method return None if it doesn't have a date, just so it's not lying. In the specific case where we need a date, I think filling in a sensible fake value as close to the place we need it as possible is the thing to do. Here, maybe when computing cached_updated or in the template tag would be a good place.
Does that seem sensible?
There was a problem hiding this comment.
Does that seem sensible?
It does. Thanks for your comment. I'll make changes accordingly.
There was a problem hiding this comment.
Extra commits have been pushed. The Meeting.updated() method now returns Optional[datetime.datetime] instead of datetime.datetime.
Let me know if I missed anything.
P.S. I wrote some additional tests for DTSTAMP, but they need some further work. I will create a separate PR when they're ready.
|
I would suggest adding "semantic-oriented" methods to the right object. See #7197 (the later comments; it took me awhile to get to this point :) |
Good point. This one is on the |
|
@richsalz Thanks for your comment. I'll have a look at your PR, and see what can be improved here. 👍 |
| 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]: |
There was a problem hiding this comment.
why do we need the walrus? (we've been avoiding it in the project so far)
There was a problem hiding this comment.
(we've been avoiding it in the project so far)
I wasn't aware of that. We don't need it - it's just sugar. It's now removed.
The walrus operator is used a lot in bibxml-service. So I probably assumed it's okay/recommended to use elsewhere.
| 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 |
There was a problem hiding this comment.
Consider:
dts = set([timeslots_updated, sessions_updated, assignments_updated])
dts.discard(None)
return max(dts) if len(dts)>0 else None
There was a problem hiding this comment.
(this is a pretty small style/readability nit)
There was a problem hiding this comment.
I'll merge this without the above suggestion - if you agree with it, please make another PR.
There was a problem hiding this comment.
TIL discard. Thanks. I'll see if a PR is necessary.
Fixes #3849
The cut-over date is set to
1980-01-01to be consistent with the behaviour of the JS client.(in
client/agenda/Agenda.vue)