fix: more small timezone-aware fixes (#4489)
* fix: use meeting timezone for agenda_by_(room|type) * refactor: use timezone.override instead of timezone.activate/deactivate * fix: add timezone info in create-charter-newrevisiondocevents * fix: use meeting timezone for recording.html template * fix: use meeting timezone for slide submission timeslot display * fix: use meeting timezone for schedule diff template * fix: use meeting timezone for edit_meeting_timeslots_and_misc_sessions * fix: use meeting timezone for materials() view * fix: use meeting timezone for proceedings() view * refactor: force timestamps to utc in important_dates_for_meeting.ics Has no effect now, but guarantees consistency with template's assertion that its timestamps are in UTC. * fix: use meeting timezone for a couple interim meeting templates * fix: use meeting timezone for propose_session_slides template * fix: use meeting timezone for upload_session_*.html * fix: use meeting timezone for a couple session templates
This commit is contained in:
parent
00cf7cf8ca
commit
63a1baf116
|
@ -28,7 +28,7 @@ def warn(string):
|
|||
# ------------------------------------------------------------------------------
|
||||
|
||||
import re
|
||||
from datetime import datetime as Datetime
|
||||
import datetime
|
||||
|
||||
import django
|
||||
django.setup()
|
||||
|
@ -44,7 +44,7 @@ system_entity = Person.objects.get(name="(System)")
|
|||
charterdir = Path(settings.CHARTER_PATH)
|
||||
for file in charterdir.files("charter-ietf-*.txt"):
|
||||
fname = file.name
|
||||
ftime = Datetime.fromtimestamp(file.mtime)
|
||||
ftime = datetime.datetime.fromtimestamp(file.mtime, datetime.timezone.utc)
|
||||
match = re.search("^(?P<name>[a-z0-9-]+)-(?P<rev>\d\d-\d\d)\.txt$", fname)
|
||||
if match:
|
||||
name = match.group("name")
|
||||
|
|
|
@ -136,12 +136,13 @@ def materials(request, num=None):
|
|||
if meeting.number.isdigit() and int(meeting.number) > 96:
|
||||
return redirect('ietf.meeting.views.proceedings', num=meeting.number)
|
||||
else:
|
||||
return render(request, "meeting/materials_upload_closed.html", {
|
||||
'meeting_num': meeting.number,
|
||||
'begin_date': begin_date,
|
||||
'cut_off_date': cut_off_date,
|
||||
'cor_cut_off_date': cor_cut_off_date
|
||||
})
|
||||
with timezone.override(meeting.tz()):
|
||||
return render(request, "meeting/materials_upload_closed.html", {
|
||||
'meeting_num': meeting.number,
|
||||
'begin_date': begin_date,
|
||||
'cut_off_date': cut_off_date,
|
||||
'cor_cut_off_date': cor_cut_off_date
|
||||
})
|
||||
|
||||
past_cutoff_date = date_today() > meeting.get_submission_correction_date()
|
||||
|
||||
|
@ -177,20 +178,21 @@ def materials(request, num=None):
|
|||
for type_name in ProceedingsMaterialTypeName.objects.all()
|
||||
]
|
||||
|
||||
return render(request, "meeting/materials.html", {
|
||||
'meeting': meeting,
|
||||
'proceedings_materials': proceedings_materials,
|
||||
'plenaries': plenaries,
|
||||
'ietf': ietf,
|
||||
'training': training,
|
||||
'irtf': irtf,
|
||||
'iab': iab,
|
||||
'other': other,
|
||||
'cut_off_date': cut_off_date,
|
||||
'cor_cut_off_date': cor_cut_off_date,
|
||||
'submission_started': now > begin_date,
|
||||
'old': old,
|
||||
})
|
||||
with timezone.override(meeting.tz()):
|
||||
return render(request, "meeting/materials.html", {
|
||||
'meeting': meeting,
|
||||
'proceedings_materials': proceedings_materials,
|
||||
'plenaries': plenaries,
|
||||
'ietf': ietf,
|
||||
'training': training,
|
||||
'irtf': irtf,
|
||||
'iab': iab,
|
||||
'other': other,
|
||||
'cut_off_date': cut_off_date,
|
||||
'cor_cut_off_date': cor_cut_off_date,
|
||||
'submission_started': now > begin_date,
|
||||
'old': old,
|
||||
})
|
||||
|
||||
def current_materials(request):
|
||||
today = date_today()
|
||||
|
@ -282,8 +284,7 @@ def materials_editable_groups(request, num=None):
|
|||
def edit_timeslots(request, num=None):
|
||||
|
||||
meeting = get_meeting(num)
|
||||
timezone.activate(meeting.tz())
|
||||
try:
|
||||
with timezone.override(meeting.tz()):
|
||||
if request.method == 'POST':
|
||||
# handle AJAX requests
|
||||
action = request.POST.get('action')
|
||||
|
@ -337,8 +338,7 @@ def edit_timeslots(request, num=None):
|
|||
"ts_with_official_assignments": ts_with_official_assignments,
|
||||
"ts_with_any_assignments": ts_with_any_assignments,
|
||||
})
|
||||
finally:
|
||||
timezone.deactivate()
|
||||
|
||||
|
||||
class NewScheduleForm(forms.ModelForm):
|
||||
class Meta:
|
||||
|
@ -992,7 +992,7 @@ class RoomNameModelChoiceField(forms.ModelChoiceField):
|
|||
return obj.name
|
||||
|
||||
class TimeSlotForm(forms.Form):
|
||||
day = forms.TypedChoiceField(coerce=lambda t: datetime.datetime.strptime(t, "%Y-%m-%d").date())
|
||||
day = forms.TypedChoiceField(coerce=lambda t: datetime.datetime.strptime(t, "%Y-%m-%d").date()) # all dates, no tz
|
||||
time = forms.TimeField()
|
||||
duration = CustomDurationField() # this is just to make 1:30 turn into 1.5 hours instead of 1.5 minutes
|
||||
location = RoomNameModelChoiceField(queryset=Room.objects.all(), required=False, empty_label="(No location)")
|
||||
|
@ -1032,8 +1032,8 @@ class TimeSlotForm(forms.Form):
|
|||
|
||||
if timeslot:
|
||||
self.initial = {
|
||||
'day': timeslot.time.date(),
|
||||
'time': timeslot.time.time(),
|
||||
'day': timeslot.local_start_time().date(),
|
||||
'time': timeslot.local_start_time().time(),
|
||||
'duration': timeslot.duration,
|
||||
'location': timeslot.location_id,
|
||||
'show_location': timeslot.show_location,
|
||||
|
@ -1112,238 +1112,239 @@ def edit_meeting_timeslots_and_misc_sessions(request, num=None, owner=None, name
|
|||
|
||||
can_edit = has_role(request.user, 'Secretariat')
|
||||
|
||||
if request.method == 'GET' and request.GET.get('action') == "edit-timeslot":
|
||||
timeslot_pk = request.GET.get('timeslot')
|
||||
if not timeslot_pk or not timeslot_pk.isdecimal():
|
||||
raise Http404
|
||||
timeslot = get_object_or_404(timeslot_qs, pk=timeslot_pk)
|
||||
with timezone.override(meeting.tz()):
|
||||
if request.method == 'GET' and request.GET.get('action') == "edit-timeslot":
|
||||
timeslot_pk = request.GET.get('timeslot')
|
||||
if not timeslot_pk or not timeslot_pk.isdecimal():
|
||||
raise Http404
|
||||
timeslot = get_object_or_404(timeslot_qs, pk=timeslot_pk)
|
||||
|
||||
assigned_session = add_event_info_to_session_qs(Session.objects.filter(
|
||||
timeslotassignments__schedule__in=[schedule, schedule.base],
|
||||
timeslotassignments__timeslot=timeslot,
|
||||
)).first()
|
||||
assigned_session = add_event_info_to_session_qs(Session.objects.filter(
|
||||
timeslotassignments__schedule__in=[schedule, schedule.base],
|
||||
timeslotassignments__timeslot=timeslot,
|
||||
)).first()
|
||||
|
||||
timeslot.can_cancel = not assigned_session or assigned_session.current_status not in ['canceled', 'canceled', 'resched']
|
||||
timeslot.can_cancel = not assigned_session or assigned_session.current_status not in ['canceled', 'canceled', 'resched']
|
||||
|
||||
return JsonResponse({
|
||||
'form': render_to_string("meeting/edit_timeslot_form.html", {
|
||||
'timeslot_form_action': 'edit',
|
||||
'timeslot_form': TimeSlotForm(meeting, schedule, timeslot=timeslot),
|
||||
'timeslot': timeslot,
|
||||
'schedule': schedule,
|
||||
'meeting': meeting,
|
||||
'can_edit': can_edit,
|
||||
}, request=request)
|
||||
})
|
||||
return JsonResponse({
|
||||
'form': render_to_string("meeting/edit_timeslot_form.html", {
|
||||
'timeslot_form_action': 'edit',
|
||||
'timeslot_form': TimeSlotForm(meeting, schedule, timeslot=timeslot),
|
||||
'timeslot': timeslot,
|
||||
'schedule': schedule,
|
||||
'meeting': meeting,
|
||||
'can_edit': can_edit,
|
||||
}, request=request)
|
||||
})
|
||||
|
||||
scroll = request.POST.get('scroll')
|
||||
scroll = request.POST.get('scroll')
|
||||
|
||||
def redirect_with_scroll():
|
||||
url = request.get_full_path()
|
||||
if scroll and scroll.isdecimal():
|
||||
url += "#scroll={}".format(scroll)
|
||||
return HttpResponseRedirect(url)
|
||||
def redirect_with_scroll():
|
||||
url = request.get_full_path()
|
||||
if scroll and scroll.isdecimal():
|
||||
url += "#scroll={}".format(scroll)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
add_timeslot_form = None
|
||||
if request.method == 'POST' and request.POST.get('action') == 'add-timeslot' and can_edit:
|
||||
add_timeslot_form = TimeSlotForm(meeting, schedule, request.POST)
|
||||
if add_timeslot_form.is_valid():
|
||||
c = add_timeslot_form.cleaned_data
|
||||
add_timeslot_form = None
|
||||
if request.method == 'POST' and request.POST.get('action') == 'add-timeslot' and can_edit:
|
||||
add_timeslot_form = TimeSlotForm(meeting, schedule, request.POST)
|
||||
if add_timeslot_form.is_valid():
|
||||
c = add_timeslot_form.cleaned_data
|
||||
|
||||
timeslot, created = TimeSlot.objects.get_or_create(
|
||||
meeting=meeting,
|
||||
type=c['type'],
|
||||
name=c['name'],
|
||||
time=meeting.tz().localize(datetime.datetime.combine(c['day'], c['time'])),
|
||||
duration=c['duration'],
|
||||
location=c['location'],
|
||||
show_location=c['show_location'],
|
||||
)
|
||||
timeslot, created = TimeSlot.objects.get_or_create(
|
||||
meeting=meeting,
|
||||
type=c['type'],
|
||||
name=c['name'],
|
||||
time=meeting.tz().localize(datetime.datetime.combine(c['day'], c['time'])),
|
||||
duration=c['duration'],
|
||||
location=c['location'],
|
||||
show_location=c['show_location'],
|
||||
)
|
||||
|
||||
if timeslot.type_id != 'regular':
|
||||
if not created:
|
||||
Session.objects.filter(timeslotassignments__timeslot=timeslot).delete()
|
||||
|
||||
session = Session.objects.create(
|
||||
meeting=meeting,
|
||||
name=c['name'],
|
||||
short=c['short'],
|
||||
group=c['group'],
|
||||
type=c['type'],
|
||||
purpose=c['purpose'],
|
||||
agenda_note=c.get('agenda_note') or "",
|
||||
)
|
||||
|
||||
SchedulingEvent.objects.create(
|
||||
session=session,
|
||||
status=SessionStatusName.objects.get(slug='sched'),
|
||||
by=request.user.person,
|
||||
)
|
||||
|
||||
SchedTimeSessAssignment.objects.create(
|
||||
timeslot=timeslot,
|
||||
session=session,
|
||||
schedule=schedule
|
||||
)
|
||||
|
||||
return redirect_with_scroll()
|
||||
|
||||
edit_timeslot_form = None
|
||||
if request.method == 'POST' and request.POST.get('action') == 'edit-timeslot' and can_edit:
|
||||
timeslot_pk = request.POST.get('timeslot')
|
||||
if not timeslot_pk or not timeslot_pk.isdecimal():
|
||||
raise Http404
|
||||
|
||||
timeslot = get_object_or_404(TimeSlot, pk=timeslot_pk)
|
||||
|
||||
edit_timeslot_form = TimeSlotForm(meeting, schedule, request.POST, timeslot=timeslot)
|
||||
if edit_timeslot_form.is_valid() and edit_timeslot_form.active_assignment.schedule_id == schedule.pk:
|
||||
|
||||
c = edit_timeslot_form.cleaned_data
|
||||
|
||||
timeslot.type = c['type']
|
||||
timeslot.name = c['name']
|
||||
timeslot.time = meeting.tz().localize(datetime.datetime.combine(c['day'], c['time']))
|
||||
timeslot.duration = c['duration']
|
||||
timeslot.location = c['location']
|
||||
timeslot.show_location = c['show_location']
|
||||
timeslot.save()
|
||||
|
||||
session = Session.objects.filter(
|
||||
timeslotassignments__schedule__in=[schedule, schedule.base if schedule else None],
|
||||
timeslotassignments__timeslot=timeslot,
|
||||
).select_related('group').first()
|
||||
|
||||
if session:
|
||||
if timeslot.type_id != 'regular':
|
||||
session.name = c['name']
|
||||
session.short = c['short']
|
||||
session.group = c['group']
|
||||
session.type = c['type']
|
||||
session.agenda_note = c.get('agenda_note') or ""
|
||||
session.save()
|
||||
|
||||
return redirect_with_scroll()
|
||||
|
||||
if request.method == 'POST' and request.POST.get('action') == 'cancel-timeslot' and can_edit:
|
||||
timeslot_pk = request.POST.get('timeslot')
|
||||
if not timeslot_pk or not timeslot_pk.isdecimal():
|
||||
raise Http404
|
||||
|
||||
timeslot = get_object_or_404(TimeSlot, pk=timeslot_pk)
|
||||
if timeslot.type_id != 'break':
|
||||
sessions = add_event_info_to_session_qs(
|
||||
Session.objects.filter(timeslotassignments__schedule=schedule, timeslotassignments__timeslot=timeslot),
|
||||
).exclude(current_status__in=['canceled', 'resched'])
|
||||
for session in sessions:
|
||||
SchedulingEvent.objects.create(
|
||||
session=session,
|
||||
status=SessionStatusName.objects.get(slug='canceled'),
|
||||
by=request.user.person,
|
||||
)
|
||||
|
||||
return redirect_with_scroll()
|
||||
|
||||
if request.method == 'POST' and request.POST.get('action') == 'delete-timeslot' and can_edit:
|
||||
timeslot_pk = request.POST.get('timeslot')
|
||||
if not timeslot_pk or not timeslot_pk.isdecimal():
|
||||
raise Http404
|
||||
|
||||
timeslot = get_object_or_404(TimeSlot, pk=timeslot_pk)
|
||||
|
||||
if timeslot.type_id != 'regular':
|
||||
if not created:
|
||||
Session.objects.filter(timeslotassignments__timeslot=timeslot).delete()
|
||||
for session in Session.objects.filter(timeslotassignments__schedule=schedule, timeslotassignments__timeslot=timeslot):
|
||||
for doc in session.materials.all():
|
||||
doc.set_state(State.objects.get(type=doc.type_id, slug='deleted'))
|
||||
e = DocEvent(doc=doc, rev=doc.rev, by=request.user.person, type='deleted')
|
||||
e.desc = "Deleted meeting session"
|
||||
e.save()
|
||||
|
||||
session = Session.objects.create(
|
||||
session.delete()
|
||||
|
||||
timeslot.delete()
|
||||
|
||||
return redirect_with_scroll()
|
||||
|
||||
sessions_by_pk = {
|
||||
s.pk: s for s in
|
||||
add_event_info_to_session_qs(
|
||||
Session.objects.filter(
|
||||
meeting=meeting,
|
||||
name=c['name'],
|
||||
short=c['short'],
|
||||
group=c['group'],
|
||||
type=c['type'],
|
||||
purpose=c['purpose'],
|
||||
agenda_note=c.get('agenda_note') or "",
|
||||
)
|
||||
).order_by('pk'),
|
||||
requested_time=True,
|
||||
requested_by=True,
|
||||
).filter(
|
||||
current_status__in=['appr', 'schedw', 'scheda', 'sched', 'canceled', 'canceledpa', 'resched']
|
||||
).prefetch_related(
|
||||
'group', 'group', 'group__type',
|
||||
)
|
||||
}
|
||||
|
||||
SchedulingEvent.objects.create(
|
||||
session=session,
|
||||
status=SessionStatusName.objects.get(slug='sched'),
|
||||
by=request.user.person,
|
||||
)
|
||||
assignments_by_timeslot = defaultdict(list)
|
||||
for a in SchedTimeSessAssignment.objects.filter(schedule__in=[schedule, schedule.base]):
|
||||
assignments_by_timeslot[a.timeslot_id].append(a)
|
||||
|
||||
SchedTimeSessAssignment.objects.create(
|
||||
timeslot=timeslot,
|
||||
session=session,
|
||||
schedule=schedule
|
||||
)
|
||||
days = [meeting.date + datetime.timedelta(days=i) for i in range(meeting.days)]
|
||||
|
||||
return redirect_with_scroll()
|
||||
timeslots_by_day_and_room = defaultdict(list)
|
||||
for t in timeslot_qs:
|
||||
timeslots_by_day_and_room[(t.time.date(), t.location_id)].append(t)
|
||||
|
||||
edit_timeslot_form = None
|
||||
if request.method == 'POST' and request.POST.get('action') == 'edit-timeslot' and can_edit:
|
||||
timeslot_pk = request.POST.get('timeslot')
|
||||
if not timeslot_pk or not timeslot_pk.isdecimal():
|
||||
raise Http404
|
||||
# Calculate full time range for display in meeting-local time, always showing at least 8am to 10pm
|
||||
min_time = min([t.local_start_time().time() for t in timeslot_qs] + [datetime.time(8)])
|
||||
max_time = max([t.local_end_time().time() for t in timeslot_qs] + [datetime.time(22)])
|
||||
min_max_delta = datetime.datetime.combine(meeting.date, max_time) - datetime.datetime.combine(meeting.date, min_time)
|
||||
|
||||
timeslot = get_object_or_404(TimeSlot, pk=timeslot_pk)
|
||||
day_grid = []
|
||||
for d in days:
|
||||
room_timeslots = []
|
||||
for r in rooms:
|
||||
ts = []
|
||||
for t in timeslots_by_day_and_room.get((d, r.pk), []):
|
||||
# FIXME: the database (as of 2020) contains spurious
|
||||
# regular timeslots in rooms not intended for regular
|
||||
# sessions - once those are gone, this filter can go
|
||||
# away
|
||||
if t.type_id == 'regular' and not any(t.slug == 'regular' for t in r.session_types.all()):
|
||||
continue
|
||||
|
||||
edit_timeslot_form = TimeSlotForm(meeting, schedule, request.POST, timeslot=timeslot)
|
||||
if edit_timeslot_form.is_valid() and edit_timeslot_form.active_assignment.schedule_id == schedule.pk:
|
||||
t.assigned_sessions = []
|
||||
for a in assignments_by_timeslot.get(t.pk, []):
|
||||
s = sessions_by_pk.get(a.session_id)
|
||||
if s:
|
||||
t.assigned_sessions.append(s)
|
||||
|
||||
c = edit_timeslot_form.cleaned_data
|
||||
local_start_dt = t.local_start_time()
|
||||
local_min_dt = local_start_dt.replace(
|
||||
hour=min_time.hour,
|
||||
minute=min_time.minute,
|
||||
second=min_time.second,
|
||||
microsecond=min_time.microsecond,
|
||||
)
|
||||
t.left_offset = 100.0 * (local_start_dt - local_min_dt) / min_max_delta
|
||||
t.layout_width = min(100.0 * t.duration / min_max_delta, 100 - t.left_offset)
|
||||
ts.append(t)
|
||||
|
||||
timeslot.type = c['type']
|
||||
timeslot.name = c['name']
|
||||
timeslot.time = meeting.tz().localize(datetime.datetime.combine(c['day'], c['time']))
|
||||
timeslot.duration = c['duration']
|
||||
timeslot.location = c['location']
|
||||
timeslot.show_location = c['show_location']
|
||||
timeslot.save()
|
||||
room_timeslots.append((r, ts))
|
||||
|
||||
session = Session.objects.filter(
|
||||
timeslotassignments__schedule__in=[schedule, schedule.base if schedule else None],
|
||||
timeslotassignments__timeslot=timeslot,
|
||||
).select_related('group').first()
|
||||
day_grid.append({
|
||||
'day': d,
|
||||
'room_timeslots': room_timeslots
|
||||
})
|
||||
|
||||
if session:
|
||||
if timeslot.type_id != 'regular':
|
||||
session.name = c['name']
|
||||
session.short = c['short']
|
||||
session.group = c['group']
|
||||
session.type = c['type']
|
||||
session.agenda_note = c.get('agenda_note') or ""
|
||||
session.save()
|
||||
|
||||
return redirect_with_scroll()
|
||||
|
||||
if request.method == 'POST' and request.POST.get('action') == 'cancel-timeslot' and can_edit:
|
||||
timeslot_pk = request.POST.get('timeslot')
|
||||
if not timeslot_pk or not timeslot_pk.isdecimal():
|
||||
raise Http404
|
||||
|
||||
timeslot = get_object_or_404(TimeSlot, pk=timeslot_pk)
|
||||
if timeslot.type_id != 'break':
|
||||
sessions = add_event_info_to_session_qs(
|
||||
Session.objects.filter(timeslotassignments__schedule=schedule, timeslotassignments__timeslot=timeslot),
|
||||
).exclude(current_status__in=['canceled', 'resched'])
|
||||
for session in sessions:
|
||||
SchedulingEvent.objects.create(
|
||||
session=session,
|
||||
status=SessionStatusName.objects.get(slug='canceled'),
|
||||
by=request.user.person,
|
||||
)
|
||||
|
||||
return redirect_with_scroll()
|
||||
|
||||
if request.method == 'POST' and request.POST.get('action') == 'delete-timeslot' and can_edit:
|
||||
timeslot_pk = request.POST.get('timeslot')
|
||||
if not timeslot_pk or not timeslot_pk.isdecimal():
|
||||
raise Http404
|
||||
|
||||
timeslot = get_object_or_404(TimeSlot, pk=timeslot_pk)
|
||||
|
||||
if timeslot.type_id != 'regular':
|
||||
for session in Session.objects.filter(timeslotassignments__schedule=schedule, timeslotassignments__timeslot=timeslot):
|
||||
for doc in session.materials.all():
|
||||
doc.set_state(State.objects.get(type=doc.type_id, slug='deleted'))
|
||||
e = DocEvent(doc=doc, rev=doc.rev, by=request.user.person, type='deleted')
|
||||
e.desc = "Deleted meeting session"
|
||||
e.save()
|
||||
|
||||
session.delete()
|
||||
|
||||
timeslot.delete()
|
||||
|
||||
return redirect_with_scroll()
|
||||
|
||||
sessions_by_pk = {
|
||||
s.pk: s for s in
|
||||
add_event_info_to_session_qs(
|
||||
Session.objects.filter(
|
||||
meeting=meeting,
|
||||
).order_by('pk'),
|
||||
requested_time=True,
|
||||
requested_by=True,
|
||||
).filter(
|
||||
current_status__in=['appr', 'schedw', 'scheda', 'sched', 'canceled', 'canceledpa', 'resched']
|
||||
).prefetch_related(
|
||||
'group', 'group', 'group__type',
|
||||
)
|
||||
}
|
||||
|
||||
assignments_by_timeslot = defaultdict(list)
|
||||
for a in SchedTimeSessAssignment.objects.filter(schedule__in=[schedule, schedule.base]):
|
||||
assignments_by_timeslot[a.timeslot_id].append(a)
|
||||
|
||||
days = [meeting.date + datetime.timedelta(days=i) for i in range(meeting.days)]
|
||||
|
||||
timeslots_by_day_and_room = defaultdict(list)
|
||||
for t in timeslot_qs:
|
||||
timeslots_by_day_and_room[(t.time.date(), t.location_id)].append(t)
|
||||
|
||||
# Calculate full time range for display in meeting-local time, always showing at least 8am to 10pm
|
||||
min_time = min([t.local_start_time().time() for t in timeslot_qs] + [datetime.time(8)])
|
||||
max_time = max([t.local_end_time().time() for t in timeslot_qs] + [datetime.time(22)])
|
||||
min_max_delta = datetime.datetime.combine(meeting.date, max_time) - datetime.datetime.combine(meeting.date, min_time)
|
||||
|
||||
day_grid = []
|
||||
for d in days:
|
||||
room_timeslots = []
|
||||
for r in rooms:
|
||||
ts = []
|
||||
for t in timeslots_by_day_and_room.get((d, r.pk), []):
|
||||
# FIXME: the database (as of 2020) contains spurious
|
||||
# regular timeslots in rooms not intended for regular
|
||||
# sessions - once those are gone, this filter can go
|
||||
# away
|
||||
if t.type_id == 'regular' and not any(t.slug == 'regular' for t in r.session_types.all()):
|
||||
continue
|
||||
|
||||
t.assigned_sessions = []
|
||||
for a in assignments_by_timeslot.get(t.pk, []):
|
||||
s = sessions_by_pk.get(a.session_id)
|
||||
if s:
|
||||
t.assigned_sessions.append(s)
|
||||
|
||||
local_start_dt = t.local_start_time()
|
||||
local_min_dt = local_start_dt.replace(
|
||||
hour=min_time.hour,
|
||||
minute=min_time.minute,
|
||||
second=min_time.second,
|
||||
microsecond=min_time.microsecond,
|
||||
)
|
||||
t.left_offset = 100.0 * (local_start_dt - local_min_dt) / min_max_delta
|
||||
t.layout_width = min(100.0 * t.duration / min_max_delta, 100 - t.left_offset)
|
||||
ts.append(t)
|
||||
|
||||
room_timeslots.append((r, ts))
|
||||
|
||||
day_grid.append({
|
||||
'day': d,
|
||||
'room_timeslots': room_timeslots
|
||||
return render(request, "meeting/edit_meeting_timeslots_and_misc_sessions.html", {
|
||||
'meeting': meeting,
|
||||
'schedule': schedule,
|
||||
'can_edit': can_edit,
|
||||
'day_grid': day_grid,
|
||||
'empty_timeslot_form': TimeSlotForm(meeting, schedule),
|
||||
'add_timeslot_form': add_timeslot_form,
|
||||
'edit_timeslot_form': edit_timeslot_form,
|
||||
'scroll': scroll,
|
||||
'hide_menu': True,
|
||||
})
|
||||
|
||||
return render(request, "meeting/edit_meeting_timeslots_and_misc_sessions.html", {
|
||||
'meeting': meeting,
|
||||
'schedule': schedule,
|
||||
'can_edit': can_edit,
|
||||
'day_grid': day_grid,
|
||||
'empty_timeslot_form': TimeSlotForm(meeting, schedule),
|
||||
'add_timeslot_form': add_timeslot_form,
|
||||
'edit_timeslot_form': edit_timeslot_form,
|
||||
'scroll': scroll,
|
||||
'hide_menu': True,
|
||||
})
|
||||
|
||||
|
||||
class SchedulePropertiesForm(forms.ModelForm):
|
||||
class Meta:
|
||||
|
@ -1565,8 +1566,7 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
|
|||
is_current_meeting = (num is None) or (num == get_current_ietf_meeting_num())
|
||||
|
||||
display_timezone = 'UTC' if utc else meeting.time_zone
|
||||
timezone.activate(display_timezone)
|
||||
try:
|
||||
with timezone.override(display_timezone):
|
||||
rendered_page = render(
|
||||
request,
|
||||
"meeting/" + base + ext,
|
||||
|
@ -1585,8 +1585,6 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
|
|||
},
|
||||
content_type=mimetype[ext],
|
||||
)
|
||||
finally:
|
||||
timezone.deactivate()
|
||||
|
||||
return rendered_page
|
||||
|
||||
|
@ -1868,13 +1866,14 @@ def agenda_by_room(request, num=None, name=None, owner=None):
|
|||
schedule__in=[schedule, schedule.base if schedule else None]
|
||||
).prefetch_related('timeslot', 'timeslot__location', 'session', 'session__group', 'session__group__parent')
|
||||
|
||||
ss_by_day = OrderedDict()
|
||||
for day in assignments.dates('timeslot__time','day'):
|
||||
ss_by_day[day]=[]
|
||||
ss_by_day = {}
|
||||
for ss in assignments.order_by('timeslot__location__functional_name','timeslot__location__name','timeslot__time'):
|
||||
day = ss.timeslot.time.date()
|
||||
ss_by_day[day].append(ss)
|
||||
return render(request,"meeting/agenda_by_room.html",{"meeting":meeting,"schedule":schedule,"ss_by_day":ss_by_day})
|
||||
# sorts by time within each day but days are not in order at this point
|
||||
day = ss.timeslot.time.astimezone(meeting.tz()).date()
|
||||
ss_by_day.setdefault(day, []).append(ss)
|
||||
ss_by_day = OrderedDict((key, ss_by_day[key]) for key in sorted(ss_by_day)) # fix day ordering
|
||||
with timezone.override(meeting.tz()):
|
||||
return render(request,"meeting/agenda_by_room.html",{"meeting":meeting,"schedule":schedule,"ss_by_day":ss_by_day})
|
||||
|
||||
@role_required('Area Director','Secretariat','IAB')
|
||||
def agenda_by_type(request, num=None, type=None, name=None, owner=None):
|
||||
|
@ -1892,7 +1891,8 @@ def agenda_by_type(request, num=None, type=None, name=None, owner=None):
|
|||
|
||||
if type:
|
||||
assignments = assignments.filter(session__type__slug=type)
|
||||
return render(request,"meeting/agenda_by_type.html",{"meeting":meeting,"schedule":schedule,"assignments":assignments})
|
||||
with timezone.override(meeting.tz()):
|
||||
return render(request,"meeting/agenda_by_type.html",{"meeting":meeting,"schedule":schedule,"assignments":assignments})
|
||||
|
||||
@role_required('Area Director','Secretariat','IAB')
|
||||
def agenda_by_type_ics(request,num=None,type=None):
|
||||
|
@ -3779,20 +3779,21 @@ def proceedings(request, num=None):
|
|||
meeting_sessions.append(s)
|
||||
ietf_areas.append((area, meeting_sessions, not_meeting_sessions))
|
||||
|
||||
return render(request, "meeting/proceedings.html", {
|
||||
'meeting': meeting,
|
||||
'plenaries': plenaries, 'ietf': ietf, 'training': training, 'irtf': irtf, 'iab': iab,
|
||||
'ietf_areas': ietf_areas,
|
||||
'cut_off_date': cut_off_date,
|
||||
'cor_cut_off_date': cor_cut_off_date,
|
||||
'submission_started': now > begin_date,
|
||||
'cache_version': cache_version,
|
||||
'attendance': meeting.get_attendance(),
|
||||
'meetinghost_logo': {
|
||||
'max_height': settings.MEETINGHOST_LOGO_MAX_DISPLAY_HEIGHT,
|
||||
'max_width': settings.MEETINGHOST_LOGO_MAX_DISPLAY_WIDTH,
|
||||
}
|
||||
})
|
||||
with timezone.override(meeting.tz()):
|
||||
return render(request, "meeting/proceedings.html", {
|
||||
'meeting': meeting,
|
||||
'plenaries': plenaries, 'ietf': ietf, 'training': training, 'irtf': irtf, 'iab': iab,
|
||||
'ietf_areas': ietf_areas,
|
||||
'cut_off_date': cut_off_date,
|
||||
'cor_cut_off_date': cor_cut_off_date,
|
||||
'submission_started': now > begin_date,
|
||||
'cache_version': cache_version,
|
||||
'attendance': meeting.get_attendance(),
|
||||
'meetinghost_logo': {
|
||||
'max_height': settings.MEETINGHOST_LOGO_MAX_DISPLAY_HEIGHT,
|
||||
'max_width': settings.MEETINGHOST_LOGO_MAX_DISPLAY_WIDTH,
|
||||
}
|
||||
})
|
||||
|
||||
@role_required('Secretariat')
|
||||
def finalize_proceedings(request, num=None):
|
||||
|
@ -4099,8 +4100,7 @@ def edit_timeslot(request, num, slot_id):
|
|||
meeting = get_object_or_404(Meeting, number=num)
|
||||
if timeslot.meeting != meeting:
|
||||
raise Http404()
|
||||
timezone.activate(meeting.tz()) # specifies current_timezone used for rendering and form handling
|
||||
try:
|
||||
with timezone.override(meeting.tz()): # specifies current_timezone used for rendering and form handling
|
||||
if request.method == 'POST':
|
||||
form = TimeSlotEditForm(instance=timeslot, data=request.POST)
|
||||
if form.is_valid():
|
||||
|
@ -4118,8 +4118,6 @@ def edit_timeslot(request, num, slot_id):
|
|||
{'timeslot': timeslot, 'form': form, 'sessions': sessions},
|
||||
status=400 if form.errors else 200,
|
||||
)
|
||||
finally:
|
||||
timezone.deactivate()
|
||||
|
||||
|
||||
@role_required('Secretariat')
|
||||
|
@ -4318,7 +4316,7 @@ def approve_proposed_slides(request, slidesubmission_id, num):
|
|||
}
|
||||
form = ApproveSlidesForm(show_apply_to_all_checkbox, initial=initial )
|
||||
|
||||
return render(request, "meeting/approve_proposed_slides.html",
|
||||
return render(request, "meeting/approve_proposed_slides.html",
|
||||
{'submission': submission,
|
||||
'session_number': session_number,
|
||||
'existing_doc' : existing_doc,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load staticfiles tz %}
|
||||
|
||||
{% block title %}Proceedings{% endblock %}
|
||||
|
||||
|
@ -59,7 +59,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
{% if sessions %}
|
||||
<tbody>
|
||||
<tbody>{% timezone meeting.time_zone %}
|
||||
|
||||
{% for session in sessions %}
|
||||
{% if session.recordings %}
|
||||
|
@ -84,7 +84,7 @@
|
|||
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
{% endtimezone %}</tbody>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div> <!-- inline-group -->
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin static django_bootstrap5 %}
|
||||
{% load origin static django_bootstrap5 tz %}
|
||||
{% block title %}
|
||||
Approve Slides Proposed for {{ submission.session.meeting }} : {{ submission.session.group.acronym }}
|
||||
{% endblock %}
|
||||
|
@ -15,7 +15,7 @@
|
|||
</h1>
|
||||
{% if session_number %}
|
||||
<h2>
|
||||
Session {{ session_number }} : {{ submission.session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi" }}
|
||||
Session {{ session_number }} : {{ submission.session.official_timeslotassignment.timeslot.time|timezone:submission.session.meeting.time_zone|date:"D M-d-Y Hi" }}
|
||||
</h2>
|
||||
{% endif %}
|
||||
<p class="alert alert-info my-3">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{# Copyright The IETF Trust 2020, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load ietf_filters %}
|
||||
{% load django_bootstrap5 %}
|
||||
{% load django_bootstrap5 tz %}
|
||||
{% block title %}Differences between Meeting Agendas for IETF {{ meeting.number }}{% endblock %}
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
|
@ -26,7 +26,7 @@
|
|||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody>{% timezone meeting.time_zone %}
|
||||
{% for d in diffs %}
|
||||
<tr>
|
||||
<td>
|
||||
|
@ -40,7 +40,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% endtimezone %}</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
No differences in scheduled sessions found.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{% for d in meeting.important_dates %}BEGIN:VEVENT
|
||||
{% load tz %}{% for d in meeting.important_dates %}BEGIN:VEVENT
|
||||
UID:ietf-{{ meeting.number }}-{{ d.name_id }}-{{ d.date.isoformat }}
|
||||
SUMMARY:IETF {{ meeting.number }}: {{ d.name.name }}
|
||||
CLASS:PUBLIC
|
||||
DTSTART{% if not d.midnight_cutoff %};VALUE=DATE{% endif %}:{{ d.date|date:"Ymd" }}{% if d.midnight_cutoff %}235900Z{% endif %}
|
||||
DTSTAMP:{{ meeting.cached_updated|date:"Ymd" }}T{{ meeting.cached_updated|date:"His" }}Z
|
||||
DTSTAMP:{{ meeting.cached_updated|utc|date:"Ymd" }}T{{ meeting.cached_updated|utc|date:"His" }}Z
|
||||
TRANSP:TRANSPARENT
|
||||
DESCRIPTION:{{ d.name.desc }}{% if first and d.name.slug == 'openreg' or first and d.name.slug == 'earlybird' %}\n
|
||||
Register here: https://www.ietf.org/how/meetings/register/{% endif %}{% if d.name.slug == 'opensched' %}\n
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% load ietf_filters %}
|
||||
{% load ietf_filters tz %}{% timezone meeting.time_zone %}
|
||||
---------------------------------------------------------
|
||||
{{ group.type.verbose_name }} Name: {{ group.name|safe }}
|
||||
{% if group.type.slug == "wg" or group.type.slug == "directorate" or group.type.slug == "team" %}Area Name: {{ group.parent }}
|
||||
|
@ -17,3 +17,4 @@ Remote Participation Information: {{ session.remote_instructions }}
|
|||
Agenda Note: {{ session.agenda_note }}
|
||||
{% endfor %}
|
||||
---------------------------------------------------------
|
||||
{% endtimezone %}
|
|
@ -1,7 +1,7 @@
|
|||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load static %}
|
||||
{% load textfilters %}
|
||||
{% load textfilters tz %}
|
||||
{% origin %}
|
||||
{% with item=session.official_timeslotassignment acronym=session.historic_group.acronym %}
|
||||
<div role="group" class="btn-group btn-group-sm">
|
||||
|
@ -90,8 +90,8 @@
|
|||
{# iCalendar item #}
|
||||
<a class="btn btn-outline-primary"
|
||||
href="{% url 'ietf.meeting.views.agenda_ical' num=meeting.number session_id=session.id %}"
|
||||
aria-label="icalendar entry for {{ acronym }} session on {{ item.timeslot.utc_start_time|date:'Y-m-d H:i' }} UTC"
|
||||
title="icalendar entry for {{ acronym }} session on {{ item.timeslot.utc_start_time|date:'Y-m-d H:i' }} UTC">
|
||||
aria-label="icalendar entry for {{ acronym }} session on {{ item.timeslot.time|utc|date:'Y-m-d H:i' }} UTC"
|
||||
title="icalendar entry for {{ acronym }} session on {{ item.timeslot.time|utc|date:'Y-m-d H:i' }} UTC">
|
||||
<i class="bi bi-calendar"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin static django_bootstrap5 %}
|
||||
{% load origin static django_bootstrap5 tz %}
|
||||
{% block title %}Propose Slides for {{ session.meeting }} : {{ session.group.acronym }}{% endblock %}
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
|
@ -13,7 +13,7 @@
|
|||
</h1>
|
||||
{% if session_number %}
|
||||
<h2 class="mt-3">
|
||||
Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi" }}
|
||||
Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|timezone:session.meeting.time_zone|date:"D M-d-Y Hi" }}
|
||||
</h2>
|
||||
{% endif %}
|
||||
<p class="alert alert-info my-3">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% load origin %}
|
||||
{% load static %}
|
||||
{% load textfilters %}
|
||||
{% load ietf_filters %}
|
||||
{% load ietf_filters tz %}
|
||||
{% origin %}
|
||||
{% if item and item|should_show_agenda_session_buttons %}
|
||||
{% with slug=item.slug %}
|
||||
|
@ -130,8 +130,8 @@
|
|||
<a class="btn btn-outline-primary"
|
||||
role="button"
|
||||
href="{% url 'ietf.meeting.views.agenda_ical' num=meeting.number session_id=session.id %}"
|
||||
aria-label="icalendar entry for {{ acronym }} session on {{ timeslot.utc_start_time|date:'Y-m-d H:i' }} UTC"
|
||||
title="icalendar entry for {{ acronym }} session on {{ timeslot.utc_start_time|date:'Y-m-d H:i' }} UTC">
|
||||
aria-label="icalendar entry for {{ acronym }} session on {{ timeslot.time|utc|date:'Y-m-d H:i' }} UTC"
|
||||
title="icalendar entry for {{ acronym }} session on {{ timeslot.time|utc|date:'Y-m-d H:i' }} UTC">
|
||||
<i class="bi bi-calendar"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
{% if sessions|length > 1 %}Session {{ forloop.counter }} :{% endif %}
|
||||
{% for time in session.times %}
|
||||
{% if not forloop.first %},{% endif %}
|
||||
{{ time|dateformat:"l Y-m-d H:i T" }}
|
||||
{{ time|timezone:session.meeting.time_zone|dateformat:"l Y-m-d H:i T" }}
|
||||
{% if time.tzinfo.zone != "UTC" %}<span class="small">({{ time|utc|dateformat:"H:i T" }})</span>{% endif %}
|
||||
{% endfor %}
|
||||
{% if session.cancelled %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin static django_bootstrap5 %}
|
||||
{% load origin static django_bootstrap5 tz %}
|
||||
{% block title %}
|
||||
{% if agenda_sp %}
|
||||
Revise
|
||||
|
@ -24,7 +24,7 @@
|
|||
</small>
|
||||
</h1>
|
||||
{% if session_number %}
|
||||
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi" }}</h2>
|
||||
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|timezone:session.meeting.time_zone|date:"D M-d-Y Hi" }}</h2>
|
||||
{% endif %}
|
||||
<form enctype="multipart/form-data" method="post" class="my-3">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin static django_bootstrap5 %}
|
||||
{% load origin static django_bootstrap5 tz %}
|
||||
{% block title %}
|
||||
{% if bluesheet_sp %}
|
||||
Revise
|
||||
|
@ -24,7 +24,7 @@
|
|||
</small>
|
||||
</h1>
|
||||
{% if session_number %}
|
||||
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi" }}</h2>
|
||||
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|timezone:session.meeting.time_zone|date:"D M-d-Y Hi" }}</h2>
|
||||
{% endif %}
|
||||
<form enctype="multipart/form-data" method="post" class="my-3">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin static django_bootstrap5 %}
|
||||
{% load origin static django_bootstrap5 tz %}
|
||||
{% block title %}
|
||||
{% if minutes_sp %}
|
||||
Revise
|
||||
|
@ -24,7 +24,7 @@
|
|||
</small>
|
||||
</h1>
|
||||
{% if session_number %}
|
||||
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi" }}</h2>
|
||||
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|timezone:session.meeting.time_zone|date:"D M-d-Y Hi" }}</h2>
|
||||
{% endif %}
|
||||
<form enctype="multipart/form-data" method="post" class="my-3">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin static django_bootstrap5 %}
|
||||
{% load origin static django_bootstrap5 tz %}
|
||||
{% block title %}
|
||||
{% if slides_sp %}
|
||||
Revise
|
||||
|
@ -25,7 +25,7 @@
|
|||
</small>
|
||||
</h1>
|
||||
{% if session_number %}
|
||||
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|date:"D M-d-Y Hi" }}</h2>
|
||||
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|timezone:session.meeting.time_zone|date:"D M-d-Y Hi" }}</h2>
|
||||
{% endif %}
|
||||
{% if slides_sp %}<h3>{{ slides_sp.document.name }}</h3>{% endif %}
|
||||
<form class="my-3" enctype="multipart/form-data" method="post">
|
||||
|
|
Loading…
Reference in a new issue