refactor: clarify handling what groups looked like at past session times (#4767)

* refactor: clarify handling what groups looked like at past session times

* fix: replace missed instance of historic_parent reference

* fix: reflect that group_at_the_time always returns something

* chore: update copyright lines
This commit is contained in:
Robert Sparks 2022-11-29 11:08:16 -06:00 committed by GitHub
parent 6293e61287
commit 0b3f9f5dc5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 134 additions and 125 deletions

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2015-2020, All Rights Reserved
# Copyright The IETF Trust 2015-2022, All Rights Reserved
import datetime
import debug # pyflakes:ignore
import factory
@ -78,16 +78,38 @@ class GroupHistoryFactory(factory.django.DjangoModelFactory):
class Meta:
model=GroupHistory
name = factory.LazyAttribute(lambda obj: obj.group.name)
state_id = 'active'
type_id = factory.LazyAttribute(lambda obj: obj.group.type_id)
list_email = factory.LazyAttribute(lambda obj: '%s@ietf.org'% obj.group.acronym)
uses_milestone_dates = True
used_roles = [] # type: List[str]
time = lambda: timezone.now()
group = factory.SubFactory(GroupFactory, state_id='active')
group = factory.SubFactory(GroupFactory)
name = factory.LazyAttribute(lambda obj: obj.group.name)
state_id = factory.LazyAttribute(lambda obj: obj.group.state_id)
type_id = factory.LazyAttribute(lambda obj: obj.group.type_id)
parent = factory.LazyAttribute(lambda obj: obj.group.parent)
uses_milestone_dates = factory.LazyAttribute(lambda obj: obj.group.uses_milestone_dates)
used_roles = factory.LazyAttribute(lambda obj: obj.group.used_roles)
description = factory.LazyAttribute(lambda obj: obj.group.description)
list_email = factory.LazyAttribute(lambda obj: '%s@ietf.org'% obj.group.acronym) #TODO : move this to GroupFactory
list_subscribe = factory.LazyAttribute(lambda obj: obj.group.list_subscribe)
list_archive = factory.LazyAttribute(lambda obj: obj.group.list_archive)
comments = factory.LazyAttribute(lambda obj: obj.group.comments)
meeting_seen_as_area = factory.LazyAttribute(lambda obj: obj.group.meeting_seen_as_area)
acronym = factory.LazyAttribute(lambda obj: obj.group.acronym)
@factory.post_generation
def unused_states(obj, create, extracted, **kwargs):
if create:
if extracted:
obj.unused_states.set(extracted)
else:
obj.unused_states.set(obj.group.unused_states.all())
@factory.post_generation
def unused_tags(obj, create, extracted, **kwargs):
if create:
if extracted:
obj.unused_tags.set(extracted)
else:
obj.unused_tags.set(obj.group.unused_states.all())
class RoleHistoryFactory(factory.django.DjangoModelFactory):
class Meta:
model=RoleHistory

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2013-2020, All Rights Reserved
# Copyright The IETF Trust 2013-2022, All Rights Reserved
# -*- coding: utf-8 -*-
@ -22,7 +22,6 @@ from django.utils import timezone
import debug # pyflakes:ignore
from ietf.doc.models import Document
from ietf.group.models import Group
from ietf.group.utils import can_manage_some_groups, can_manage_group
from ietf.ietfauth.utils import has_role, user_is_person
from ietf.liaisons.utils import get_person_for_user
@ -32,7 +31,6 @@ from ietf.meeting.models import Meeting, Schedule, TimeSlot, SchedTimeSessAssign
from ietf.meeting.utils import session_requested_by, add_event_info_to_session_qs
from ietf.name.models import ImportantDateName, SessionPurposeName
from ietf.utils import log, meetecho
from ietf.utils.history import find_history_replacements_active_at
from ietf.utils.mail import send_mail
from ietf.utils.pipe import pipe
from ietf.utils.text import xslugify
@ -92,8 +90,6 @@ def preprocess_assignments_for_agenda(assignments_queryset, meeting, extra_prefe
For each assignment a, adds
a.start_timestamp
a.end_timestamp
a.session.historic_group
a.session.historic_parent
a.session.rescheduled_to (if rescheduled)
a.session.prefetched_active_materials
a.session.order_number
@ -119,16 +115,10 @@ def preprocess_assignments_for_agenda(assignments_queryset, meeting, extra_prefe
# assignments = list(assignments_queryset) # make sure we're set in stone
assignments = assignments_queryset
# meeting_time is meeting-local midnight at the start of the meeting date
meeting_time = meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time())
)
# replace groups with historic counterparts
groups = [ ]
for a in assignments:
if a.session:
a.session.historic_group = None
a.session.order_number = None
if a.session.group and a.session.group not in groups:
@ -139,30 +129,15 @@ def preprocess_assignments_for_agenda(assignments_queryset, meeting, extra_prefe
if a.session and a.session.group:
sessions_for_groups[(a.session.group, a.session.type_id)].append(a)
group_replacements = find_history_replacements_active_at(groups, meeting_time)
parent_id_set = set()
for a in assignments:
if a.session and a.session.group:
a.session.historic_group = group_replacements.get(a.session.group_id)
if a.session.historic_group:
a.session.historic_group.historic_parent = None
if a.session.historic_group.parent_id:
parent_id_set.add(a.session.historic_group.parent_id)
l = sessions_for_groups.get((a.session.group, a.session.type_id), [])
a.session.order_number = l.index(a) + 1 if a in l else 0
parents = Group.objects.filter(pk__in=parent_id_set)
parent_replacements = find_history_replacements_active_at(parents, meeting_time)
timeslot_by_session_pk = {a.session_id: a.timeslot for a in assignments}
for a in assignments:
if a.session and a.session.historic_group and a.session.historic_group.parent_id:
a.session.historic_group.historic_parent = parent_replacements.get(a.session.historic_group.parent_id)
if a.session.current_status == 'resched':
a.session.rescheduled_to = timeslot_by_session_pk.get(a.session.tombstone_for_id)
@ -211,12 +186,15 @@ class AgendaKeywordTool:
@staticmethod
def _get_group(s):
"""Get group of a session, handling historic groups"""
return getattr(s, 'historic_group', s.group)
return s.group_at_the_time()
def _get_group_parent(self, s):
"""Get parent of a group or parent of a session's group, handling historic groups"""
g = self._get_group(s) if isinstance(s, Session) else s # accept a group or a session arg
return getattr(g, 'historic_parent', g.parent)
if isinstance(s, Session):
return s.group_parent_at_the_time()
else:
# Assumption is that s is a group...
return s and s.parent
def _purpose_keyword(self, purpose):
"""Get the keyword corresponding to a session purpose"""
@ -240,8 +218,10 @@ class AgendaFilterOrganizer(AgendaKeywordTool):
Either assignments or sessions must be specified (but not both). Keywords should be applied
to these items before calling either of the 'get_' methods, otherwise some special filters
may not be included (e.g., 'BoF' or 'Plenary'). If historic_group and/or historic_parent
attributes are present, these will be used instead of group/parent.
may not be included (e.g., 'BoF' or 'Plenary'). If the session's group has a GroupHistory
object active at the time of the start of the session's meeting, and/or the session's group
parent had an active GroupHistory object active at the time, these will be used instead of
the group or parent.
The organizer will process its inputs once, when one of its get_ methods is first called.

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2007-2020, All Rights Reserved
# Copyright The IETF Trust 2007-2022, All Rights Reserved
# -*- coding: utf-8 -*-
@ -39,6 +39,7 @@ from ietf.name.models import (
)
from ietf.person.models import Person
from ietf.utils.decorators import memoize
from ietf.utils.history import find_history_replacements_active_at
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
from ietf.utils.text import xslugify
from ietf.utils.timezone import datetime_from_date, date_today
@ -408,6 +409,19 @@ class Meeting(models.Model):
def uses_notes(self):
return self.date>=datetime.date(2020,7,6)
def groups_at_the_time(self):
if not hasattr(self,'cached_groups_at_the_time'):
all_group_pks = set(self.session_set.values_list('group__pk', flat=True))
all_group_pks.update(self.session_set.values_list('group__parent__pk', flat=True))
all_group_pks.discard(None)
# meeting_time is meeting-local midnight at the start of the meeting date
meeting_start = self.tz().localize(
datetime.datetime.combine(self.date, datetime.time())
)
self.cached_groups_at_the_time = find_history_replacements_active_at(Group.objects.filter(pk__in=all_group_pks), meeting_start)
return self.cached_groups_at_the_time
class Meta:
ordering = ["-date", "-id"]
indexes = [
@ -824,12 +838,12 @@ class SchedTimeSessAssignment(models.Model):
if not self.timeslot:
components.append("unknown")
if not self.session or not (getattr(self.session, "historic_group", None) or self.session.group):
if not self.session or not self.session.group_at_the_time():
components.append("unknown")
else:
components.append(self.timeslot.time.strftime("%Y-%m-%d-%a-%H%M"))
g = getattr(self.session, "historic_group", None) or self.session.group
g = self.session.group_at_the_time()
if self.timeslot.type.slug in ('break', 'reg', 'other'):
components.append(g.acronym)
@ -839,7 +853,7 @@ class SchedTimeSessAssignment(models.Model):
if self.timeslot.type.slug == "plenary":
components.append("1plenary")
else:
p = getattr(g, "historic_parent", None) or g.parent
p = self.session.group_parent_at_the_time()
if p and p.type_id in ("area", "irtf", 'ietf'):
components.append(p.acronym)
@ -1263,10 +1277,8 @@ class Session(models.Model):
def chat_room_name(self):
if self.type_id=='plenary':
return 'plenary'
elif hasattr(self, 'historic_group'):
return self.historic_group.acronym
else:
return self.group.acronym
return self.group_at_the_time().acronym
def chat_room_url(self):
return settings.CHAT_URL_PATTERN.format(chat_room_name=self.chat_room_name())
@ -1288,6 +1300,13 @@ class Session(models.Model):
def notes_url(self):
return urljoin(settings.IETF_NOTES_URL, self.notes_id())
def group_at_the_time(self):
return self.meeting.groups_at_the_time()[self.group.pk]
def group_parent_at_the_time(self):
if self.group_at_the_time().parent:
return self.meeting.groups_at_the_time()[self.group_at_the_time().parent.pk]
class SchedulingEvent(models.Model):
session = ForeignKey(Session)
time = models.DateTimeField(default=timezone.now, help_text="When the event happened")

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2013-2020, All Rights Reserved
# Copyright The IETF Trust 2013-2022, All Rights Reserved
# -*- coding: utf-8 -*-
@ -72,9 +72,10 @@ def webcal_url(context, viewname, *args, **kwargs):
@register.simple_tag
def assignment_display_name(assignment):
"""Get name for an assignment"""
if assignment.session.type.slug == 'regular' and getattr(assignment.session, 'historic_group', None):
return assignment.session.historic_group.name
return assignment.session.name or assignment.timeslot.name
if assignment.session.type.slug == 'regular':
return assignment.session.group_at_the_time().name
else:
return assignment.session.name or assignment.timeslot.name
class AnchorNode(template.Node):
@ -149,4 +150,4 @@ def location_anchor(parser, token):
raise template.TemplateSyntaxError('location_anchor requires a single argument')
nodelist = parser.parse(('end_location_anchor',))
parser.delete_first_token() # delete the end tag
return LocationAnchorNode(ts_var, nodelist)
return LocationAnchorNode(ts_var, nodelist)

View file

@ -1,14 +1,16 @@
# Copyright The IETF Trust 2020, All Rights Reserved
# Copyright The IETF Trust 2020-2022, All Rights Reserved
# -*- coding: utf-8 -*-
import datetime
import debug # pyflakes:ignore
from unittest.mock import patch, Mock
from django.conf import settings
from django.contrib.messages.storage.fallback import FallbackStorage
from django.test import override_settings, RequestFactory
from ietf.group.factories import GroupFactory
from ietf.group.factories import GroupFactory, GroupHistoryFactory
from ietf.group.models import Group
from ietf.meeting.factories import SessionFactory, MeetingFactory, TimeSlotFactory
from ietf.meeting.helpers import (AgendaFilterOrganizer, AgendaKeywordTagger,
@ -28,7 +30,8 @@ class AgendaKeywordTaggerTests(TestCase):
"""Assignments should be tagged properly
The historic param can be None, group, or parent, to specify whether to test
with no historic_group, a historic_group but no historic_parent, or both.
with no GroupHistory active at the time of the Session's meeting,
with such a GroupHistory active, no GroupHistory for the parent, or both.
"""
# decide whether meeting should use legacy keywords (for office hours)
legacy_keywords = meeting_num <= 111
@ -38,14 +41,19 @@ class AgendaKeywordTaggerTests(TestCase):
group_state_id = 'bof' if bof else 'active'
group = GroupFactory(state_id=group_state_id)
# Set up the historic group and parent if needed. Keep track of these as expected_*
# for later reference. If not using historic group or parent, fall back to the non-historic
# groups.
if historic:
expected_group = GroupFactory(state_id=group_state_id)
history_time = meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time())
- datetime.timedelta(days=1)
)
expected_group = GroupHistoryFactory(group=group, time=history_time)
if historic == 'parent':
expected_area = GroupFactory(type_id='area')
expected_group.historic_parent = expected_area
expected_area = GroupHistoryFactory(group=group.parent,time=history_time)
else:
expected_area = expected_group.parent
else:
@ -112,11 +120,6 @@ class AgendaKeywordTaggerTests(TestCase):
assignments = meeting.schedule.assignments.all()
# Set up historic groups if needed.
if historic:
for a in assignments:
a.session.historic_group = expected_group
# Execute the method under test
AgendaKeywordTagger(assignments=assignments).apply()

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2007-2020, All Rights Reserved
# Copyright The IETF Trust 2007-2022, All Rights Reserved
# -*- coding: utf-8 -*-
@ -89,7 +89,6 @@ from ietf.secr.proceedings.proc_utils import (get_progress_stats, post_process,
from ietf.utils import markdown
from ietf.utils.decorators import require_api_key
from ietf.utils.hedgedoc import Note, NoteError
from ietf.utils.history import find_history_replacements_active_at
from ietf.utils.log import assertion
from ietf.utils.mail import send_mail_message, send_mail_text
from ietf.utils.mime import get_mime_type
@ -1712,15 +1711,13 @@ def agenda_extract_schedule (item):
"startDateTime": item.timeslot.time.isoformat(),
"status": item.session.current_status,
"type": item.session.type.slug,
"isBoF": item.session.historic_group.state_id == "bof",
"isBoF": item.session.group_at_the_time().state_id == "bof",
"filterKeywords": item.filter_keywords,
"groupAcronym": item.session.historic_group.acronym if item.session.historic_group else item.session.group.acronym,
"groupName": item.session.historic_group.name,
"groupAcronym": item.session.group_at_the_time().acronym,
"groupName": item.session.group_at_the_time().name,
"groupParent": {
"acronym": item.session.historic_group.parent.acronym
# "name": item.session.historic_group.parent.name,
# "description": item.session.historic_group.parent.description
} if item.session.historic_group.parent else {},
"acronym": item.session.group_parent_at_the_time().acronym
} if item.session.group_parent_at_the_time() else {},
"note": item.session.agenda_note,
"remoteInstructions": item.session.remote_instructions,
"flags": {
@ -1849,15 +1846,15 @@ def agenda_csv(schedule, filtered_assignments):
row.append("None")
row.append(item.timeslot.location.name if item.timeslot.location else "")
row.append("")
row.append(item.session.historic_group.acronym)
row.append(item.session.historic_group.historic_parent.acronym.upper() if item.session.historic_group.historic_parent else "")
row.append(item.session.group_at_the_time().acronym)
row.append(item.session.group_parent_at_the_time().acronym.upper() if item.session.group_parent_at_the_time() else "")
row.append(item.session.name)
row.append(item.session.pk)
elif item.slot_type().slug == "plenary":
row.append(item.session.name)
row.append(item.timeslot.location.name if item.timeslot.location else "")
row.append("")
row.append(item.session.historic_group.acronym if item.session.historic_group else "")
row.append(item.session.group_at_the_time().acronym)
row.append("")
row.append(item.session.name)
row.append(item.session.pk)
@ -1866,10 +1863,10 @@ def agenda_csv(schedule, filtered_assignments):
elif item.slot_type().slug == 'regular':
row.append(item.timeslot.name)
row.append(item.timeslot.location.name if item.timeslot.location else "")
row.append(item.session.historic_group.historic_parent.acronym.upper() if item.session.historic_group.historic_parent else "")
row.append(item.session.historic_group.acronym if item.session.historic_group else "")
row.append("BOF" if item.session.historic_group.state_id in ("bof", "bof-conc") else item.session.historic_group.type.name)
row.append(item.session.historic_group.name if item.session.historic_group else "")
row.append(item.session.group_parent_at_the_time().acronym.upper() if item.session.group_parent_at_the_time() else "")
row.append(item.session.group_at_the_time().acronym)
row.append("BOF" if item.session.group_at_the_time().state_id in ("bof", "bof-conc") else item.session.group_at_the_time().type.name)
row.append(item.session.group_at_the_time().name)
row.append(item.session.pk)
row.append(agenda_field(item))
row.append(slides_field(item))
@ -2029,11 +2026,7 @@ def parse_agenda_filter_params(querydict):
def should_include_assignment(filter_params, assignment):
"""Decide whether to include an assignment
When filtering by wg, uses historic_group if available as an attribute
on the session, otherwise falls back to using group.
"""
"""Decide whether to include an assignment"""
shown = len(set(filter_params['show']).intersection(assignment.filter_keywords)) > 0
hidden = len(set(filter_params['hide']).intersection(assignment.filter_keywords)) > 0
return shown and not hidden
@ -2080,7 +2073,7 @@ def agenda_ical(request, num=None, name=None, acronym=None, session_id=None):
assignments = [a for a in assignments if should_include_assignment(filt_params, a)]
if acronym:
assignments = [ a for a in assignments if a.session.historic_group and a.session.historic_group.acronym == acronym ]
assignments = [ a for a in assignments if a.session.group_at_the_time().acronym == acronym ]
elif session_id:
assignments = [ a for a in assignments if a.session_id == int(session_id) ]
@ -2119,23 +2112,23 @@ def agenda_json(request, num=None):
sessdict['objtype'] = 'session'
sessdict['id'] = asgn.pk
sessdict['is_bof'] = False
if asgn.session.historic_group:
if asgn.session.group_at_the_time():
sessdict['group'] = {
"acronym": asgn.session.historic_group.acronym,
"name": asgn.session.historic_group.name,
"type": asgn.session.historic_group.type_id,
"state": asgn.session.historic_group.state_id,
"acronym": asgn.session.group_at_the_time().acronym,
"name": asgn.session.group_at_the_time().name,
"type": asgn.session.group_at_the_time().type_id,
"state": asgn.session.group_at_the_time().state_id,
}
if asgn.session.historic_group.is_bof():
if asgn.session.group_at_the_time().is_bof():
sessdict['is_bof'] = True
if asgn.session.historic_group.type_id in ['wg','rg', 'ag', 'rag'] or asgn.session.historic_group.acronym in ['iesg',]: # TODO: should that first list be groupfeatures driven?
if asgn.session.historic_group.historic_parent:
sessdict['group']['parent'] = asgn.session.historic_group.historic_parent.acronym
parent_acronyms.add(asgn.session.historic_group.historic_parent.acronym)
if asgn.session.group_at_the_time().type_id in ['wg','rg', 'ag', 'rag'] or asgn.session.group_at_the_time().acronym in ['iesg',]: # TODO: should that first list be groupfeatures driven?
if asgn.session.group_parent_at_the_time():
sessdict['group']['parent'] = asgn.session.group_parent_at_the_time().acronym
parent_acronyms.add(asgn.session.group_parent_at_the_time().acronym)
if asgn.session.name:
sessdict['name'] = asgn.session.name
else:
sessdict['name'] = asgn.session.historic_group.name
sessdict['name'] = asgn.session.group_at_the_time().name
if asgn.session.short:
sessdict['short'] = asgn.session.short
if asgn.session.agenda_note:
@ -2291,24 +2284,9 @@ def session_details(request, num, acronym):
if not sessions:
raise Http404
# Find the time of the meeting, so that we can look back historically
# for what the group was called at the time.
meeting_time = meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time())
)
groups = list(set([ s.group for s in sessions ]))
group_replacements = find_history_replacements_active_at(groups, meeting_time)
status_names = {n.slug: n.name for n in SessionStatusName.objects.all()}
for session in sessions:
session.historic_group = None
if session.group:
session.historic_group = group_replacements.get(session.group_id)
if session.historic_group:
session.historic_group.historic_parent = None
session.type_counter = Counter()
ss = session.timeslotassignments.filter(schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None]).order_by('timeslot__time')
if ss:
@ -3475,9 +3453,6 @@ def upcoming(request):
)
).filter(current_status__in=('sched','canceled'))
for session in interim_sessions:
session.historic_group = session.group
# Set up for agenda filtering - only one filter_category here
AgendaKeywordTagger(sessions=interim_sessions).apply()
filter_organizer = AgendaFilterOrganizer(sessions=interim_sessions, single_category=True)

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2012-2020, All Rights Reserved
# Copyright The IETF Trust 2012-2022, All Rights Reserved
# -*- coding: utf-8 -*-
@ -2476,6 +2476,7 @@ class rfc8989EligibilityTests(TestCase):
p1 = RoleHistoryFactory(
name_id='chair',
group__time=day_before,
group__state_id='active',
group__group__state_id='conclude',
).person
eligible.append(p1)
@ -2483,6 +2484,7 @@ class rfc8989EligibilityTests(TestCase):
p2 = RoleHistoryFactory(
name_id='secr',
group__time=year_before,
group__state_id='active',
group__group__state_id='conclude',
).person
eligible.append(p2)
@ -2490,6 +2492,7 @@ class rfc8989EligibilityTests(TestCase):
p3 = RoleHistoryFactory(
name_id='secr',
group__time=just_after_three_years_before,
group__state_id='active',
group__group__state_id='conclude',
).person
eligible.append(p3)
@ -2497,6 +2500,7 @@ class rfc8989EligibilityTests(TestCase):
p4 = RoleHistoryFactory(
name_id='chair',
group__time=three_years_before,
group__state_id='active',
group__group__state_id='conclude',
).person
eligible.append(p4)
@ -2504,6 +2508,7 @@ class rfc8989EligibilityTests(TestCase):
p5 = RoleHistoryFactory(
name_id='chair',
group__time=just_before_three_years_before,
group__state_id='active',
group__group__state_id='conclude',
).person
ineligible.append(p5)
@ -2723,12 +2728,16 @@ class VolunteerDecoratorUnitTests(TestCase):
nomcom.volunteer_set.create(person=meeting_person)
office_person = PersonFactory()
history_time = datetime_from_date(elig_date) - datetime.timedelta(days=365)
RoleHistoryFactory(
name_id='chair',
group__time=datetime_from_date(elig_date) - datetime.timedelta(days=365),
group__time=history_time,
group__group__time=history_time,
group__state_id='active',
group__group__state_id='conclude',
person=office_person,
)
nomcom.volunteer_set.create(person=office_person)
author_person = PersonFactory()

View file

@ -2,11 +2,11 @@
VERSION:2.0
METHOD:PUBLISH
PRODID:-//IETF//datatracker.ietf.org ical agenda//EN
{{schedule.meeting.vtimezone}}{% for item in assignments %}{% if item.session.historic_group %}BEGIN:VEVENT
{{schedule.meeting.vtimezone}}{% for item in assignments %}BEGIN:VEVENT
UID:ietf-{{schedule.meeting.number}}-{{item.timeslot.pk}}-{{item.session.group.acronym}}
SUMMARY:{% if item.session.name %}{{item.session.name|ics_esc}}{% else %}{% if not item.session.historic_group %}{{item.timeslot.name|ics_esc}}{% else %}{{item.session.historic_group.acronym|lower}} - {{item.session.historic_group.name}}{% endif%}{%endif%}{% if item.session.agenda_note %} ({{item.session.agenda_note}}){% endif %}
SUMMARY:{% if item.session.name %}{{item.session.name|ics_esc}}{% else %}{{item.session.group_at_the_time.acronym|lower}} - {{item.session.group_at_the_time.name}}{%endif%}{% if item.session.agenda_note %} ({{item.session.agenda_note}}){% endif %}
{% if item.timeslot.show_location %}LOCATION:{{item.timeslot.get_location}}
{% endif %}STATUS:{{item.session.ical_status}}
STATUS:{{item.session.ical_status}}
CLASS:PUBLIC
DTSTART{% ics_date_time item.timeslot.local_start_time schedule.meeting.time_zone %}
DTEND{% ics_date_time item.timeslot.local_end_time schedule.meeting.time_zone %}

View file

@ -19,11 +19,11 @@
{{ item.timeslot.time_desc }} {{ item.session.name }} - {{ item.timeslot.location.name }}
{{ item.session.agenda_text.strip|indent:"3" }}
{% endif %}{% if item.slot_type.slug == 'regular' %}{% if item.session.historic_group %}{% ifchanged %}
{% endif %}{% if item.slot_type.slug == 'regular' %}{% ifchanged %}
{{ item.timeslot.time_desc }} {{ item.timeslot.name }}
{% endifchanged %}{{ item.timeslot.location.name|ljust:14 }} {{ item.session.historic_group.historic_parent.acronym|upper|ljust:4 }} {{ item.session.historic_group.acronym|ljust:10 }} {{ item.session.historic_group.name }} {% if item.session.historic_group.state_id == "bof" %}BOF{% elif item.session.historic_group.type_id == "wg" %}WG{% endif %}{% if item.session.agenda_note %} - {{ item.session.agenda_note }}{% endif %}{% if item.session.current_status == 'canceled' %} *** CANCELLED ***{% elif item.session.current_status == 'resched' %} *** RESCHEDULED{% if item.session.rescheduled_to %} TO {{ item.session.rescheduled_to.time|date:"l G:i"|upper }}-{{ item.session.rescheduled_to.end_time|date:"G:i" }}{% endif %} ***{% endif %}
{% endif %}{% endif %}{% if item.slot_type.slug == "break" %}
{% endifchanged %}{{ item.timeslot.location.name|ljust:14 }} {{ item.session.group_parent_at_the_time.acronym|upper|ljust:4 }} {{ item.session.group_at_the_time.acronym|ljust:10 }} {{ item.session.group_at_the_time.name }} {% if item.session.group_at_the_time.state_id == "bof" %}BOF{% elif item.session.group_at_the_time.type_id == "wg" %}WG{% endif %}{% if item.session.agenda_note %} - {{ item.session.agenda_note }}{% endif %}{% if item.session.current_status == 'canceled' %} *** CANCELLED ***{% elif item.session.current_status == 'resched' %} *** RESCHEDULED{% if item.session.rescheduled_to %} TO {{ item.session.rescheduled_to.time|date:"l G:i"|upper }}-{{ item.session.rescheduled_to.end_time|date:"G:i" }}{% endif %} ***{% endif %}
{% endif %}{% if item.slot_type.slug == "break" %}
{{ item.timeslot.time_desc }} {{ item.timeslot.name }}{% if schedule.meeting.break_area and item.timeslot.show_location %} - {{ schedule.meeting.break_area }}{% endif %}{% endif %}{% if item.slot_type.slug == "other" %}
{{ item.timeslot.time_desc }} {{ item.timeslot.name }} - {{ item.timeslot.location.name }}{% endif %}{% endfor %}

View file

@ -1,9 +1,9 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{# Copyright The IETF Trust 2015-2022, All Rights Reserved #}
{% load origin %}
{% load static %}
{% load textfilters tz %}
{% origin %}
{% with item=session.official_timeslotassignment acronym=session.historic_group.acronym %}
{% with item=session.official_timeslotassignment acronym=session.group_at_the_time.acronym %}
<div role="group" class="btn-group btn-group-sm">
{% if session.agenda and show_agenda %}
{% include "meeting/session_agenda_include.html" with slug=item.slug session=session timeslot=item.timeslot only %}

View file

@ -1,4 +1,4 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{# Copyright The IETF Trust 2015-2022, All Rights Reserved #}
{# expects slug, session, and timeslot to be in the context. Calling template must load the agenda_materials.js script. #}
{% load origin %}
{% origin %}
@ -19,7 +19,7 @@
{% if timeslot.type.slug == 'plenary' %}
{{ timeslot.name }}
{% else %}
{{ session.historic_group.name }}
{{ session.group_at_the_time.name }}
{% endif %}
</p>
<button type="button"