From 0706f8519f9a79db0dd24b57fe84aa4ee69fa3cc Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 9 Nov 2021 02:40:54 +0000 Subject: [PATCH] Update migrations and fix a few failing tests. Branch ready for merge. - Legacy-Id: 19571 --- ietf/doc/templatetags/ietf_filters.py | 12 ++-- .../0050_groupfeatures_agenda_filter_type.py | 20 ++++++ ...pulate_groupfeatures_agenda_filter_type.py | 35 ++++++++++ .../0052_groupfeatures_session_purposes.py | 24 +++++++ ...populate_groupfeatures_session_purposes.py | 64 +++++++++++++++++++ ietf/group/models.py | 6 +- .../migrations/0050_session_on_agenda.py | 18 ++++++ .../0051_populate_session_on_agenda.py | 39 +++++++++++ ietf/meeting/tests_js.py | 2 +- ietf/meeting/tests_views.py | 2 +- ...036_depopulate_timeslottypename_private.py | 2 +- 11 files changed, 212 insertions(+), 12 deletions(-) create mode 100644 ietf/group/migrations/0050_groupfeatures_agenda_filter_type.py create mode 100644 ietf/group/migrations/0051_populate_groupfeatures_agenda_filter_type.py create mode 100644 ietf/group/migrations/0052_groupfeatures_session_purposes.py create mode 100644 ietf/group/migrations/0053_populate_groupfeatures_session_purposes.py create mode 100644 ietf/meeting/migrations/0050_session_on_agenda.py create mode 100644 ietf/meeting/migrations/0051_populate_session_on_agenda.py diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index 48ee2b4a9..f4b472c35 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -694,21 +694,21 @@ def is_special_agenda_item(assignment): def should_show_agenda_session_buttons(assignment): """Should this agenda item show the session buttons (jabber link, etc)? - In IETF-111 and earlier, office hours sessions were designated by a name ending + In IETF-112 and earlier, office hours sessions were designated by a name ending with ' office hours' and belonged to the IESG or some other group. This led to incorrect session buttons being displayed. Suppress session buttons for - when name ends with 'office hours' in the pre-111 meetings. + when name ends with 'office hours' in the pre-112 meetings. >>> from collections import namedtuple # use to build mock objects >>> mock_meeting = namedtuple('t3', ['number']) >>> mock_session = namedtuple('t2', ['name']) >>> mock_assignment = namedtuple('t1', ['meeting', 'session']) # meeting must be a callable >>> factory = lambda num, name: mock_assignment(session=mock_session(name), meeting=lambda: mock_meeting(num)) - >>> test_cases = [('105', 'acme office hours'), ('111', 'acme office hours')] + >>> test_cases = [('105', 'acme office hours'), ('112', 'acme office hours')] >>> any(should_show_agenda_session_buttons(factory(*tc)) for tc in test_cases) False - >>> test_cases = [('interim-2020-acme-112', 'acme'), ('112', 'acme'), ('150', 'acme'), ('105', 'acme'),] - >>> test_cases.extend([('111', 'acme'), ('interim-2020-acme-112', 'acme office hours')]) - >>> test_cases.extend([('112', 'acme office hours'), ('150', 'acme office hours')]) + >>> test_cases = [('interim-2020-acme-113', 'acme'), ('113', 'acme'), ('150', 'acme'), ('105', 'acme'),] + >>> test_cases.extend([('112', 'acme'), ('interim-2020-acme-113', 'acme office hours')]) + >>> test_cases.extend([('113', 'acme office hours'), ('150', 'acme office hours')]) >>> all(should_show_agenda_session_buttons(factory(*tc)) for tc in test_cases) True """ diff --git a/ietf/group/migrations/0050_groupfeatures_agenda_filter_type.py b/ietf/group/migrations/0050_groupfeatures_agenda_filter_type.py new file mode 100644 index 000000000..e7cdc64de --- /dev/null +++ b/ietf/group/migrations/0050_groupfeatures_agenda_filter_type.py @@ -0,0 +1,20 @@ +# Copyright The IETF Trust 2021 All Rights Reserved + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('name', '0033_populate_agendafiltertypename'), + ('group', '0049_auto_20211019_1136'), + ] + + operations = [ + migrations.AddField( + model_name='groupfeatures', + name='agenda_filter_type', + field=models.ForeignKey(default='none', on_delete=django.db.models.deletion.PROTECT, to='name.AgendaFilterTypeName'), + ), + ] diff --git a/ietf/group/migrations/0051_populate_groupfeatures_agenda_filter_type.py b/ietf/group/migrations/0051_populate_groupfeatures_agenda_filter_type.py new file mode 100644 index 000000000..fa5025902 --- /dev/null +++ b/ietf/group/migrations/0051_populate_groupfeatures_agenda_filter_type.py @@ -0,0 +1,35 @@ +# Copyright The IETF Trust 2021 All Rights Reserved + +from django.db import migrations + + +def forward(apps, schema_editor): + GroupFeatures = apps.get_model('group', 'GroupFeatures') + + # map AgendaFilterTypeName slug to group types - unlisted get 'none' + filter_types = dict( + # list previously hard coded in agenda view, plus 'review' + normal={'wg', 'ag', 'rg', 'rag', 'iab', 'program', 'review'}, + heading={'area', 'ietf', 'irtf'}, + special={'team', 'adhoc'}, + ) + + for ft, group_types in filter_types.items(): + for gf in GroupFeatures.objects.filter(type__slug__in=group_types): + gf.agenda_filter_type_id = ft + gf.save() + + +def reverse(apps, schema_editor): + pass # nothing to do, model will be deleted anyway + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0050_groupfeatures_agenda_filter_type'), + ] + + operations = [ + migrations.RunPython(forward, reverse), + ] diff --git a/ietf/group/migrations/0052_groupfeatures_session_purposes.py b/ietf/group/migrations/0052_groupfeatures_session_purposes.py new file mode 100644 index 000000000..f6f73799f --- /dev/null +++ b/ietf/group/migrations/0052_groupfeatures_session_purposes.py @@ -0,0 +1,24 @@ +# Copyright The IETF Trust 2021 All Rights Reserved + +# Generated by Django 2.2.24 on 2021-09-26 11:29 + +from django.db import migrations +import ietf.group.models +import ietf.name.models +import jsonfield.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0051_populate_groupfeatures_agenda_filter_type'), + ('name', '0034_sessionpurposename'), + ] + + operations = [ + migrations.AddField( + model_name='groupfeatures', + name='session_purposes', + field=jsonfield.fields.JSONField(default=[], help_text='Allowed session purposes for this group type', max_length=256, validators=[ietf.group.models.JSONForeignKeyListValidator(ietf.name.models.SessionPurposeName)]), + ), + ] diff --git a/ietf/group/migrations/0053_populate_groupfeatures_session_purposes.py b/ietf/group/migrations/0053_populate_groupfeatures_session_purposes.py new file mode 100644 index 000000000..642aa5f21 --- /dev/null +++ b/ietf/group/migrations/0053_populate_groupfeatures_session_purposes.py @@ -0,0 +1,64 @@ +# Copyright The IETF Trust 2021 All Rights Reserved + +# Generated by Django 2.2.24 on 2021-09-26 11:29 + +from django.db import migrations + + +default_purposes = dict( + adhoc=['presentation'], + adm=['closed_meeting', 'officehours'], + ag=['regular'], + area=['regular'], + dir=['open_meeting', 'presentation', 'regular', 'social', 'tutorial'], + iab=['closed_meeting', 'regular'], + iabasg=['closed_meeting', 'officehours', 'open_meeting'], + iana=['officehours'], + iesg=['closed_meeting', 'open_meeting'], + ietf=['admin', 'plenary', 'presentation', 'social'], + irtf=[], + ise=['officehours'], + isoc=['officehours', 'open_meeting', 'presentation'], + nomcom=['closed_meeting', 'officehours'], + program=['regular', 'tutorial'], + rag=['regular'], + review=['open_meeting', 'social'], + rfcedtyp=['officehours'], + rg=['regular'], + team=['coding', 'presentation', 'social', 'tutorial'], + wg=['regular'], +) + + +def forward(apps, schema_editor): + GroupFeatures = apps.get_model('group', 'GroupFeatures') + SessionPurposeName = apps.get_model('name', 'SessionPurposeName') + + # verify that we're not about to use an invalid purpose + for purposes in default_purposes.values(): + for purpose in purposes: + SessionPurposeName.objects.get(pk=purpose) # throws an exception unless exists + + for type_, purposes in default_purposes.items(): + GroupFeatures.objects.filter( + type=type_ + ).update( + session_purposes=purposes + ) + +def reverse(apps, schema_editor): + GroupFeatures = apps.get_model('group', 'GroupFeatures') + GroupFeatures.objects.update(session_purposes=[]) # clear back out to default + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0052_groupfeatures_session_purposes'), + ('name', '0035_populate_sessionpurposename'), + + ] + + operations = [ + migrations.RunPython(forward, reverse), + ] diff --git a/ietf/group/models.py b/ietf/group/models.py index 007ca5485..7deb0b392 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -17,7 +17,7 @@ from django.dispatch import receiver import debug # pyflakes:ignore from ietf.group.colors import fg_group_colors, bg_group_colors -from ietf.name.models import (GroupStateName, GroupTypeName, DocTagName, GroupMilestoneStateName, RoleName, +from ietf.name.models import (GroupStateName, GroupTypeName, DocTagName, GroupMilestoneStateName, RoleName, AgendaTypeName, AgendaFilterTypeName, ExtResourceName, SessionPurposeName) from ietf.person.models import Email, Person from ietf.utils.db import IETFJSONField @@ -264,8 +264,8 @@ class GroupFeatures(models.Model): groupman_authroles = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["Secretariat",]) matman_roles = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["ad","chair","delegate","secr"]) role_order = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["chair","secr","member"], - help_text="The order in which roles are shown, for instance on photo pages. Enter valid JSON.") - session_purposes = jsonfield.JSONField(max_length=256, blank=False, default=[], + help_text="The order in which roles are shown, for instance on photo pages. Enter valid JSON.") + session_purposes = IETFJSONField(max_length=256, accepted_empty_values=[[], {}], blank=False, default=[], help_text="Allowed session purposes for this group type", validators=[JSONForeignKeyListValidator(SessionPurposeName)]) diff --git a/ietf/meeting/migrations/0050_session_on_agenda.py b/ietf/meeting/migrations/0050_session_on_agenda.py new file mode 100644 index 000000000..15f1885ae --- /dev/null +++ b/ietf/meeting/migrations/0050_session_on_agenda.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.24 on 2021-10-22 06:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0049_session_purpose'), + ] + + operations = [ + migrations.AddField( + model_name='session', + name='on_agenda', + field=models.BooleanField(default=True, help_text='Is this session visible on the meeting agenda?'), + ), + ] diff --git a/ietf/meeting/migrations/0051_populate_session_on_agenda.py b/ietf/meeting/migrations/0051_populate_session_on_agenda.py new file mode 100644 index 000000000..b63942072 --- /dev/null +++ b/ietf/meeting/migrations/0051_populate_session_on_agenda.py @@ -0,0 +1,39 @@ +# Generated by Django 2.2.24 on 2021-10-22 06:58 + +from django.db import migrations, models + + +def forward(apps, schema_editor): + Session = apps.get_model('meeting', 'Session') + SchedTimeSessAssignment = apps.get_model('meeting', 'SchedTimeSessAssignment') + # find official assignments that are to private timeslots and fill in session.on_agenda + private_assignments = SchedTimeSessAssignment.objects.filter( + models.Q( + schedule=models.F('session__meeting__schedule') + ) | models.Q( + schedule=models.F('session__meeting__schedule__base') + ), + timeslot__type__private=True, + ) + for pa in private_assignments: + pa.session.on_agenda = False + pa.session.save() + # Also update any sessions to match their purpose's default setting (this intentionally + # overrides the timeslot settings above, but that is unlikely to matter because the + # purposes will roll out at the same time as the on_agenda field) + Session.objects.filter(purpose__on_agenda=False).update(on_agenda=False) + Session.objects.filter(purpose__on_agenda=True).update(on_agenda=True) + +def reverse(apps, schema_editor): + Session = apps.get_model('meeting', 'Session') + Session.objects.update(on_agenda=True) # restore all to default on_agenda=True state + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0050_session_on_agenda'), + ] + + operations = [ + migrations.RunPython(forward, reverse), + ] diff --git a/ietf/meeting/tests_js.py b/ietf/meeting/tests_js.py index e7455994c..6f94b5298 100644 --- a/ietf/meeting/tests_js.py +++ b/ietf/meeting/tests_js.py @@ -766,7 +766,7 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): all of the events needed by the editor. """ # Set up a meeting and a schedule a plain user can edit - schedule = ScheduleFactory(owner__user__username="plain") + schedule = ScheduleFactory(meeting__type_id='ietf', owner__user__username="plain") meeting = schedule.meeting # Open the editor diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index 4eb951a5c..450c7e1e4 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -3714,7 +3714,7 @@ class EditTests(TestCase): self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created') def test_edit_session(self): - session = SessionFactory(group__type_id='team') # type determines allowed session purposes + session = SessionFactory(meeting__type_id='ietf', group__type_id='team') # type determines allowed session purposes self.client.login(username='secretary', password='secretary+password') url = urlreverse('ietf.meeting.views.edit_session', kwargs={'session_id': session.pk}) r = self.client.get(url) diff --git a/ietf/name/migrations/0036_depopulate_timeslottypename_private.py b/ietf/name/migrations/0036_depopulate_timeslottypename_private.py index 6f1db8e76..352ab8d58 100644 --- a/ietf/name/migrations/0036_depopulate_timeslottypename_private.py +++ b/ietf/name/migrations/0036_depopulate_timeslottypename_private.py @@ -23,7 +23,7 @@ class Migration(migrations.Migration): dependencies = [ ('name', '0035_populate_sessionpurposename'), - ('meeting', '0050_populate_session_on_agenda'), + ('meeting', '0051_populate_session_on_agenda'), ] operations = [