From a5f27b0a5bf4ca2c9db25abe3d4b36ea47152e1d Mon Sep 17 00:00:00 2001 From: Valery Smyslov Date: Tue, 26 Jul 2022 20:02:09 +0300 Subject: [PATCH] feat: show expired WG/RG drafts at WG/RG Documents page (#4252) * Show expired WG/RG drafts. * Update 0009_add_group_exp_rule_to_groups.py fix dependency on migration file name * Update forms.py Simplify condition statements * Update views.py Fix - remove erroneous check (never happen) * Added tests for expired WG drafts filtering rule --- ietf/community/forms.py | 9 ++++-- .../migrations/0008_add_group_exp_rule.py | 18 ++++++++++++ .../0009_add_group_exp_rule_to_groups.py | 29 +++++++++++++++++++ ietf/community/models.py | 1 + ietf/community/tests.py | 13 +++++++++ ietf/community/utils.py | 4 ++- ietf/group/tests_info.py | 17 ++++++++++- ietf/group/utils.py | 6 ++++ ietf/group/views.py | 8 +++-- 9 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 ietf/community/migrations/0008_add_group_exp_rule.py create mode 100644 ietf/community/migrations/0009_add_group_exp_rule_to_groups.py diff --git a/ietf/community/forms.py b/ietf/community/forms.py index cbf8d24f7..a8709c787 100644 --- a/ietf/community/forms.py +++ b/ietf/community/forms.py @@ -38,9 +38,12 @@ class SearchRuleForm(forms.ModelForm): f.initial = f.queryset[0].pk f.widget = forms.HiddenInput() - if rule_type in ['group', 'group_rfc', 'area', 'area_rfc']: - restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active") - + if rule_type in ["group", "group_rfc", "area", "area_rfc", "group_exp"]: + if rule_type == "group_exp": + restrict_state("draft", "expired") + else: + restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active") + if rule_type.startswith("area"): self.fields["group"].label = "Area" self.fields["group"].queryset = self.fields["group"].queryset.filter(Q(type="area") | Q(acronym="irtf")).order_by("acronym") diff --git a/ietf/community/migrations/0008_add_group_exp_rule.py b/ietf/community/migrations/0008_add_group_exp_rule.py new file mode 100644 index 000000000..b5ddebe63 --- /dev/null +++ b/ietf/community/migrations/0008_add_group_exp_rule.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2022-06-30 05:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('community', '0007_remove_docs2_m2m'), + ] + + operations = [ + migrations.AlterField( + model_name='searchrule', + name='rule_type', + field=models.CharField(choices=[('group', 'All I-Ds associated with a particular group'), ('area', 'All I-Ds associated with all groups in a particular Area'), ('group_rfc', 'All RFCs associated with a particular group'), ('area_rfc', 'All RFCs associated with all groups in a particular Area'), ('group_exp', 'All expired I-Ds of a particular group'), ('state_iab', 'All I-Ds that are in a particular IAB state'), ('state_iana', 'All I-Ds that are in a particular IANA state'), ('state_iesg', 'All I-Ds that are in a particular IESG state'), ('state_irtf', 'All I-Ds that are in a particular IRTF state'), ('state_ise', 'All I-Ds that are in a particular ISE state'), ('state_rfceditor', 'All I-Ds that are in a particular RFC Editor state'), ('state_ietf', 'All I-Ds that are in a particular Working Group state'), ('author', 'All I-Ds with a particular author'), ('author_rfc', 'All RFCs with a particular author'), ('ad', 'All I-Ds with a particular responsible AD'), ('shepherd', 'All I-Ds with a particular document shepherd'), ('name_contains', 'All I-Ds with particular text/regular expression in the name')], max_length=30), + ), + ] diff --git a/ietf/community/migrations/0009_add_group_exp_rule_to_groups.py b/ietf/community/migrations/0009_add_group_exp_rule_to_groups.py new file mode 100644 index 000000000..b2a9ae53c --- /dev/null +++ b/ietf/community/migrations/0009_add_group_exp_rule_to_groups.py @@ -0,0 +1,29 @@ +# Generated by Django 2.2.28 on 2022-06-30 23:15 + + +from django.db import migrations + + +def forward(apps, schema_editor): + SearchRule = apps.get_model('community', 'SearchRule') + Group = apps.get_model('group', 'Group') + State = apps.get_model('doc', 'State') + for group in Group.objects.filter(type_id__in=['wg','rg'], state_id='active'): + Rule = SearchRule.objects.filter(group = group) + if Rule.exists(): + SearchRule.objects.create(community_list=Rule[0].community_list, rule_type="group_exp", group=group, state=State.objects.get(slug="expired", type="draft"),) + + +def reverse(apps, schema_editor): + SearchRule = apps.get_model('community', 'SearchRule') + SearchRule.objects.filter(rule_type='group_exp').delete() + + +class Migration(migrations.Migration): + dependencies = [ + ('community', '0008_add_group_exp_rule'), + ] + + operations = [ + migrations.RunPython(forward, reverse), + ] diff --git a/ietf/community/models.py b/ietf/community/models.py index aa34fd89d..8938b1c09 100644 --- a/ietf/community/models.py +++ b/ietf/community/models.py @@ -45,6 +45,7 @@ class SearchRule(models.Model): ('area', 'All I-Ds associated with all groups in a particular Area'), ('group_rfc', 'All RFCs associated with a particular group'), ('area_rfc', 'All RFCs associated with all groups in a particular Area'), + ('group_exp', 'All expired I-Ds of a particular group'), ('state_iab', 'All I-Ds that are in a particular IAB state'), ('state_iana', 'All I-Ds that are in a particular IANA state'), diff --git a/ietf/community/tests.py b/ietf/community/tests.py index d01745f99..3dd86f70e 100644 --- a/ietf/community/tests.py +++ b/ietf/community/tests.py @@ -52,6 +52,8 @@ class CommunityListTests(WebTest): rule_shepherd = SearchRule.objects.create(rule_type="shepherd", state=State.objects.get(type="draft", slug="active"), person=draft.shepherd.person, community_list=clist) + rule_group_exp = SearchRule.objects.create(rule_type="group_exp", group=draft.group, state=State.objects.get(type="draft", slug="expired"), community_list=clist) + rule_name_contains = SearchRule.objects.create(rule_type="name_contains", state=State.objects.get(type="draft", slug="active"), text="draft-.*" + "-".join(draft.name.split("-")[2:]), community_list=clist) reset_name_contains_index_for_rule(rule_name_contains) @@ -65,6 +67,7 @@ class CommunityListTests(WebTest): self.assertTrue(rule_ad in matching_rules) self.assertTrue(rule_shepherd in matching_rules) self.assertTrue(rule_name_contains in matching_rules) + self.assertTrue(rule_group_exp not in matching_rules) # rule -> docs self.assertTrue(draft in list(docs_matching_community_list_rule(rule_group))) @@ -75,6 +78,16 @@ class CommunityListTests(WebTest): self.assertTrue(draft in list(docs_matching_community_list_rule(rule_ad))) self.assertTrue(draft in list(docs_matching_community_list_rule(rule_shepherd))) self.assertTrue(draft in list(docs_matching_community_list_rule(rule_name_contains))) + self.assertTrue(draft not in list(docs_matching_community_list_rule(rule_group_exp))) + + draft.set_state(State.objects.get(type='draft', slug='expired')) + + # doc -> rules + matching_rules = list(community_list_rules_matching_doc(draft)) + self.assertTrue(rule_group_exp in matching_rules) + + # rule -> docs + self.assertTrue(draft in list(docs_matching_community_list_rule(rule_group_exp))) def test_view_list(self): PersonFactory(user__username='plain') diff --git a/ietf/community/utils.py b/ietf/community/utils.py index accb3056e..06da50011 100644 --- a/ietf/community/utils.py +++ b/ietf/community/utils.py @@ -75,6 +75,8 @@ def docs_matching_community_list_rule(rule): docs = Document.objects.all() if rule.rule_type in ['group', 'area', 'group_rfc', 'area_rfc']: return docs.filter(Q(group=rule.group_id) | Q(group__parent=rule.group_id), states=rule.state) + elif rule.rule_type in ['group_exp']: + return docs.filter(group=rule.group_id, states=rule.state) elif rule.rule_type.startswith("state_"): return docs.filter(states=rule.state) elif rule.rule_type in ["author", "author_rfc"]: @@ -98,7 +100,7 @@ def community_list_rules_matching_doc(doc): if doc.group.parent_id: groups.append(doc.group.parent_id) rules |= SearchRule.objects.filter( - rule_type__in=['group', 'area', 'group_rfc', 'area_rfc'], + rule_type__in=['group', 'area', 'group_rfc', 'area_rfc', 'group_exp'], state__in=states, group__in=groups ) diff --git a/ietf/group/tests_info.py b/ietf/group/tests_info.py index 041cd65dd..0b3ead2c7 100644 --- a/ietf/group/tests_info.py +++ b/ietf/group/tests_info.py @@ -26,7 +26,7 @@ from django.utils.html import escape from ietf.community.models import CommunityList from ietf.community.utils import reset_name_contains_index_for_rule -from ietf.doc.factories import WgDraftFactory, CharterFactory, BallotDocEventFactory +from ietf.doc.factories import WgDraftFactory, IndividualDraftFactory, CharterFactory, BallotDocEventFactory from ietf.doc.models import Document, DocAlias, DocEvent, State from ietf.doc.utils_charter import charter_name_for_group from ietf.group.admin import GroupForm as AdminGroupForm @@ -212,6 +212,17 @@ class GroupPagesTests(TestCase): old_dah.time_added -= datetime.timedelta(days=173) # make an "old" action holder old_dah.save() + draft4 = WgDraftFactory(group=group) + draft4.set_state(State.objects.get(type='draft', slug='expired')) # Expired WG draft + draft5 = IndividualDraftFactory() + draft5.set_state(State.objects.get(type='draft', slug='expired')) # Expired non-WG draft + draft6 = WgDraftFactory(group=group) + draft6.set_state(State.objects.get(type='draft', slug='expired')) + draft6.set_state(State.objects.get(type='draft-iesg', slug='dead')) # Expired WG draft, marked as dead + draft7 = WgDraftFactory(group=group) + draft7.set_state(State.objects.get(type='draft', slug='expired')) + draft7.set_state(State.objects.get(type='draft-stream-%s' % draft7.stream_id, slug='dead')) # Expired WG draft, marked as dead + clist = CommunityList.objects.get(group=group) related_docs_rule = clist.searchrule_set.get(rule_type='name_contains') reset_name_contains_index_for_rule(related_docs_rule) @@ -229,6 +240,10 @@ class GroupPagesTests(TestCase): for ah in draft3.action_holders.all(): self.assertContains(r, escape(ah.name)) self.assertContains(r, 'for 173 days', count=1) # the old_dah should be tagged + self.assertContains(r, draft4.name) + self.assertNotContains(r, draft5.name) + self.assertNotContains(r, draft6.name) + self.assertNotContains(r, draft7.name) # Make sure that a logged in user is presented with an opportunity to add results to their community list self.client.login(username="secretary", password="secretary+password") diff --git a/ietf/group/utils.py b/ietf/group/utils.py index 8a5fd3728..b701d6a7c 100644 --- a/ietf/group/utils.py +++ b/ietf/group/utils.py @@ -193,6 +193,12 @@ def setup_default_community_list_for_group(group): group=group, state=State.objects.get(slug="rfc", type="draft"), ) + SearchRule.objects.create( + community_list=clist, + rule_type="group_exp", + group=group, + state=State.objects.get(slug="expired", type="draft"), + ) related_docs_rule = SearchRule.objects.create( community_list=clist, rule_type="name_contains", diff --git a/ietf/group/views.py b/ietf/group/views.py index 51ecc0e54..3a4b9f3e0 100644 --- a/ietf/group/views.py +++ b/ietf/group/views.py @@ -452,10 +452,12 @@ def prepare_group_documents(request, group, clist): # non-WG drafts and call for WG adoption are considered related if (d.group != group or (d.stream_id and d.get_state_slug("draft-stream-%s" % d.stream_id) in ("c-adopt", "wg-cand"))): - d.search_heading = "Related Internet-Draft" - docs_related.append(d) + if d.get_state_slug() != "expired": + d.search_heading = "Related Internet-Draft" + docs_related.append(d) else: - docs.append(d) + if not (d.get_state_slug('draft-iesg') == "dead" or (d.stream_id and d.get_state_slug("draft-stream-%s" % d.stream_id) == "dead")): + docs.append(d) meta_related = meta.copy()