fix: use meeting-local times for agenda editor (#4458)
* fix: use meeting-local times for agenda editor * test: test agenda editor timezone handling
This commit is contained in:
parent
aa4ba65bc5
commit
3cbcfde475
|
@ -21,6 +21,7 @@ from urllib.parse import urlparse, urlsplit, quote
|
|||
from PIL import Image
|
||||
from pathlib import Path
|
||||
from tempfile import NamedTemporaryFile
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.conf import settings
|
||||
|
@ -1558,8 +1559,6 @@ class EditMeetingScheduleTests(TestCase):
|
|||
@staticmethod
|
||||
def _right_now_in(tzinfo):
|
||||
right_now = timezone.now().astimezone(tzinfo)
|
||||
if not settings.USE_TZ:
|
||||
right_now = right_now.replace(tzinfo=None)
|
||||
return right_now
|
||||
|
||||
def test_assign_session(self):
|
||||
|
@ -1860,6 +1859,58 @@ class EditMeetingScheduleTests(TestCase):
|
|||
self.assertContains(r, 'No timeslots exist')
|
||||
self.assertContains(r, urlreverse('ietf.meeting.views.edit_timeslots', kwargs={'num': meeting.number}))
|
||||
|
||||
def test_editor_time_zone(self):
|
||||
"""Agenda editor should show meeting time zone"""
|
||||
time_zone = 'Etc/GMT+8'
|
||||
meeting_tz = ZoneInfo(time_zone)
|
||||
meeting = MeetingFactory(
|
||||
type_id='ietf',
|
||||
date=date_today(meeting_tz) + datetime.timedelta(days=7),
|
||||
populate_schedule=False,
|
||||
time_zone=time_zone,
|
||||
)
|
||||
meeting.schedule = ScheduleFactory(meeting=meeting)
|
||||
meeting.save()
|
||||
timeslot = TimeSlotFactory(meeting=meeting)
|
||||
ts_start = timeslot.time.astimezone(meeting_tz)
|
||||
ts_end = timeslot.end_time().astimezone(meeting_tz)
|
||||
url = urlreverse('ietf.meeting.views.edit_meeting_schedule', kwargs={'num': meeting.number})
|
||||
self.assertTrue(self.client.login(username='secretary', password='secretary+password'))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
pq = PyQuery(r.content)
|
||||
|
||||
day_header = pq('.day-flow .day-label')
|
||||
self.assertIn(ts_start.strftime('%A'), day_header.text())
|
||||
|
||||
day_swap = day_header.find('.swap-days')
|
||||
self.assertEqual(day_swap.attr('data-dayid'), ts_start.date().isoformat())
|
||||
self.assertEqual(day_swap.attr('data-start'), ts_start.date().isoformat())
|
||||
|
||||
time_label = pq('.day-flow .time-header .time-label')
|
||||
self.assertEqual(len(time_label), 1)
|
||||
# strftime() does not seem to support hours without leading 0, so do this manually
|
||||
time_label_string = f'{ts_start.hour:d}:{ts_start.minute:02d} - {ts_end.hour:d}:{ts_end.minute:02d}'
|
||||
self.assertIn(time_label_string, time_label.text())
|
||||
self.assertEqual(time_label.attr('data-start'), ts_start.astimezone(datetime.timezone.utc).isoformat())
|
||||
self.assertEqual(time_label.attr('data-end'), ts_end.astimezone(datetime.timezone.utc).isoformat())
|
||||
|
||||
ts_swap = time_label.find('.swap-timeslot-col')
|
||||
origin_label = ts_swap.attr('data-origin-label')
|
||||
# testing the exact date in origin_label is hard because Django's date filter uses
|
||||
# different month formats than Python's strftime, so just check a couple parts.
|
||||
self.assertIn(ts_start.strftime('%A'), origin_label)
|
||||
self.assertIn(f'{ts_start.hour:d}:{ts_start.minute:02d}-{ts_end.hour:d}:{ts_end.minute:02d}', origin_label)
|
||||
|
||||
timeslot_elt = pq(f'#timeslot{timeslot.pk}')
|
||||
self.assertEqual(len(timeslot_elt), 1)
|
||||
self.assertEqual(timeslot_elt.attr('data-start'), ts_start.astimezone(datetime.timezone.utc).isoformat())
|
||||
self.assertEqual(timeslot_elt.attr('data-end'), ts_end.astimezone(datetime.timezone.utc).isoformat())
|
||||
|
||||
timeslot_label = pq(f'#timeslot{timeslot.pk} .time-label')
|
||||
self.assertEqual(len(timeslot_label), 1)
|
||||
self.assertIn(time_label_string, timeslot_label.text())
|
||||
|
||||
|
||||
class EditTimeslotsTests(TestCase):
|
||||
def login(self, username='secretary'):
|
||||
|
|
|
@ -446,9 +446,7 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
|||
|
||||
lock_time = settings.MEETING_SESSION_LOCK_TIME
|
||||
def timeslot_locked(ts):
|
||||
meeting_now = timezone.now().astimezone(pytz.timezone(meeting.time_zone))
|
||||
if not settings.USE_TZ:
|
||||
meeting_now = meeting_now.replace(tzinfo=None)
|
||||
meeting_now = timezone.now().astimezone(meeting.tz())
|
||||
return schedule.is_official and (ts.time - meeting_now < lock_time)
|
||||
|
||||
if not can_see:
|
||||
|
@ -645,7 +643,7 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
|||
rd = room_data[t.location_id]
|
||||
rd['timeslot_count'] += 1
|
||||
rd['start_and_duration'].append((t.time, t.duration))
|
||||
ttd = t.time.date()
|
||||
ttd = t.local_start_time().date() # date in meeting timezone
|
||||
all_days.add(ttd)
|
||||
if ttd not in rd['timeslots_by_day']:
|
||||
rd['timeslots_by_day'][ttd] = []
|
||||
|
@ -830,8 +828,8 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
|||
source_day = swap_days_form.cleaned_data['source_day']
|
||||
target_day = swap_days_form.cleaned_data['target_day']
|
||||
|
||||
source_timeslots = [ts for ts in timeslots_qs if ts.time.date() == source_day]
|
||||
target_timeslots = [ts for ts in timeslots_qs if ts.time.date() == target_day]
|
||||
source_timeslots = [ts for ts in timeslots_qs if ts.local_start_time().date() == source_day]
|
||||
target_timeslots = [ts for ts in timeslots_qs if ts.local_start_time().date() == target_day]
|
||||
if any(timeslot_locked(ts) for ts in source_timeslots + target_timeslots):
|
||||
return HttpResponseBadRequest("Can't swap these days.")
|
||||
|
||||
|
@ -888,8 +886,8 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
|||
# possible timeslot start/ends
|
||||
timeslot_groups = defaultdict(set)
|
||||
for ts in timeslots_qs:
|
||||
ts.start_end_group = "ts-group-{}-{}".format(ts.time.strftime("%Y%m%d-%H%M"), int(ts.duration.total_seconds() / 60))
|
||||
timeslot_groups[ts.time.date()].add((ts.time, ts.end_time(), ts.start_end_group))
|
||||
ts.start_end_group = "ts-group-{}-{}".format(ts.local_start_time().strftime("%Y%m%d-%H%M"), int(ts.duration.total_seconds() / 60))
|
||||
timeslot_groups[ts.local_start_time().date()].add((ts.local_start_time(), ts.local_end_time(), ts.start_end_group))
|
||||
|
||||
# prepare sessions
|
||||
prepare_sessions_for_display(sessions)
|
||||
|
@ -970,22 +968,23 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
|
|||
if ts_type.slug in session_data.get('enabled_timeslot_types', [])
|
||||
]
|
||||
|
||||
return render(request, "meeting/edit_meeting_schedule.html", {
|
||||
'meeting': meeting,
|
||||
'schedule': schedule,
|
||||
'can_edit': can_edit,
|
||||
'can_edit_properties': can_edit or secretariat,
|
||||
'secretariat': secretariat,
|
||||
'days': days,
|
||||
'timeslot_groups': sorted((d, list(sorted(t_groups))) for d, t_groups in timeslot_groups.items()),
|
||||
'unassigned_sessions': unassigned_sessions,
|
||||
'session_parents': session_parents,
|
||||
'session_purposes': session_purposes,
|
||||
'timeslot_types': timeslot_types,
|
||||
'hide_menu': True,
|
||||
'lock_time': lock_time,
|
||||
'enabled_timeslot_types': enabled_timeslot_types,
|
||||
})
|
||||
with timezone.override(meeting.tz()):
|
||||
return render(request, "meeting/edit_meeting_schedule.html", {
|
||||
'meeting': meeting,
|
||||
'schedule': schedule,
|
||||
'can_edit': can_edit,
|
||||
'can_edit_properties': can_edit or secretariat,
|
||||
'secretariat': secretariat,
|
||||
'days': days,
|
||||
'timeslot_groups': sorted((d, list(sorted(t_groups))) for d, t_groups in timeslot_groups.items()),
|
||||
'unassigned_sessions': unassigned_sessions,
|
||||
'session_parents': session_parents,
|
||||
'session_purposes': session_purposes,
|
||||
'timeslot_types': timeslot_types,
|
||||
'hide_menu': True,
|
||||
'lock_time': lock_time,
|
||||
'enabled_timeslot_types': enabled_timeslot_types,
|
||||
})
|
||||
|
||||
|
||||
class RoomNameModelChoiceField(forms.ModelChoiceField):
|
||||
|
|
Loading…
Reference in a new issue