From 0b3f9f5dc5fa1ce173a337093c4dc0175ae8ade8 Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Tue, 29 Nov 2022 11:08:16 -0600 Subject: [PATCH] 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 --- ietf/group/factories.py | 38 +++++++-- ietf/meeting/helpers.py | 42 +++------- ietf/meeting/models.py | 33 ++++++-- .../templatetags/agenda_custom_tags.py | 11 +-- ietf/meeting/tests_helpers.py | 25 +++--- ietf/meeting/views.py | 77 +++++++------------ ietf/nomcom/tests.py | 13 +++- ietf/templates/meeting/agenda.ics | 6 +- ietf/templates/meeting/agenda.txt | 6 +- .../meeting/interim_session_buttons.html | 4 +- .../meeting/session_agenda_include.html | 4 +- 11 files changed, 134 insertions(+), 125 deletions(-) diff --git a/ietf/group/factories.py b/ietf/group/factories.py index e7fbef59a..0a3f7b6db 100644 --- a/ietf/group/factories.py +++ b/ietf/group/factories.py @@ -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 diff --git a/ietf/meeting/helpers.py b/ietf/meeting/helpers.py index 734c30896..fab10fad2 100644 --- a/ietf/meeting/helpers.py +++ b/ietf/meeting/helpers.py @@ -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. diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index da1ce3757..8596ad975 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -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") diff --git a/ietf/meeting/templatetags/agenda_custom_tags.py b/ietf/meeting/templatetags/agenda_custom_tags.py index 9a75b897c..962cc5a34 100644 --- a/ietf/meeting/templatetags/agenda_custom_tags.py +++ b/ietf/meeting/templatetags/agenda_custom_tags.py @@ -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) \ No newline at end of file + return LocationAnchorNode(ts_var, nodelist) diff --git a/ietf/meeting/tests_helpers.py b/ietf/meeting/tests_helpers.py index d77610baa..d9dafb7d6 100644 --- a/ietf/meeting/tests_helpers.py +++ b/ietf/meeting/tests_helpers.py @@ -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() diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 381e4f427..6fe1e95e8 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -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) diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py index 14fa4f7ef..2cc9ac328 100644 --- a/ietf/nomcom/tests.py +++ b/ietf/nomcom/tests.py @@ -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() diff --git a/ietf/templates/meeting/agenda.ics b/ietf/templates/meeting/agenda.ics index 654f8569d..bcb9827c9 100644 --- a/ietf/templates/meeting/agenda.ics +++ b/ietf/templates/meeting/agenda.ics @@ -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 %} diff --git a/ietf/templates/meeting/agenda.txt b/ietf/templates/meeting/agenda.txt index 489ebbe6a..34fd3ae73 100644 --- a/ietf/templates/meeting/agenda.txt +++ b/ietf/templates/meeting/agenda.txt @@ -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 %} diff --git a/ietf/templates/meeting/interim_session_buttons.html b/ietf/templates/meeting/interim_session_buttons.html index c4096430b..30be5c734 100644 --- a/ietf/templates/meeting/interim_session_buttons.html +++ b/ietf/templates/meeting/interim_session_buttons.html @@ -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 %}
{% if session.agenda and show_agenda %} {% include "meeting/session_agenda_include.html" with slug=item.slug session=session timeslot=item.timeslot only %} diff --git a/ietf/templates/meeting/session_agenda_include.html b/ietf/templates/meeting/session_agenda_include.html index 098b8c5a9..190b7a8a0 100644 --- a/ietf/templates/meeting/session_agenda_include.html +++ b/ietf/templates/meeting/session_agenda_include.html @@ -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 %}