From 71270621b78e47e31e4135876ec868eed3ec0c2e Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 7 Jul 2023 17:27:55 -0300 Subject: [PATCH 01/13] chore: Track RFCs if they were tracked as a draft --- ietf/community/migrations/0003_track_rfcs.py | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 ietf/community/migrations/0003_track_rfcs.py diff --git a/ietf/community/migrations/0003_track_rfcs.py b/ietf/community/migrations/0003_track_rfcs.py new file mode 100644 index 000000000..61caabb77 --- /dev/null +++ b/ietf/community/migrations/0003_track_rfcs.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.3 on 2023-07-07 18:33 + +from django.db import migrations + + +def forward(apps, schema_editor): + """Track any RFCs that were created from tracked drafts""" + CommunityList = apps.get_model("community", "CommunityList") + RelatedDocument = apps.get_model("doc", "RelatedDocument") + + for cl in CommunityList.objects.all(): + for rfc in set( + RelatedDocument.objects.filter( + source__in=cl.added_docs.all(), + relationship__slug="became_rfc", + ).values_list("target__docs", flat=True) + ): + cl.added_docs.add(rfc) + + +class Migration(migrations.Migration): + dependencies = [ + ("community", "0002_auto_20230320_1222"), + ("doc", "0010_move_rfc_docaliases"), + ] + + operations = [migrations.RunPython(forward)] From 5385760f2acf1ea01ac0fea40ff93074465d416d Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 11 Jul 2023 16:14:49 -0300 Subject: [PATCH 02/13] chore: Migrate SearchRules for the rfc state --- ietf/community/migrations/0003_track_rfcs.py | 24 ++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/ietf/community/migrations/0003_track_rfcs.py b/ietf/community/migrations/0003_track_rfcs.py index 61caabb77..77a553ec1 100644 --- a/ietf/community/migrations/0003_track_rfcs.py +++ b/ietf/community/migrations/0003_track_rfcs.py @@ -8,6 +8,7 @@ def forward(apps, schema_editor): CommunityList = apps.get_model("community", "CommunityList") RelatedDocument = apps.get_model("doc", "RelatedDocument") + # Handle individually tracked documents for cl in CommunityList.objects.all(): for rfc in set( RelatedDocument.objects.filter( @@ -17,11 +18,30 @@ def forward(apps, schema_editor): ): cl.added_docs.add(rfc) + # Handle rules + SearchRule = apps.get_model("community", "SearchRule") + State = apps.get_model("doc", "State") + draft_rfc_state = State.objects.get(type_id="draft", slug="rfc") + rfc_published_state = State.objects.get(type_id="rfc", slug="published") + SearchRule.objects.filter(state=draft_rfc_state).update(state=rfc_published_state) + + +def reverse(apps, schema_editor): + Document = apps.get_model("doc", "Document") + for rfc in Document.objects.filter(type__slug="rfc"): + rfc.communitylist_set.clear() + + SearchRule = apps.get_model("community", "SearchRule") + State = apps.get_model("doc", "State") + draft_rfc_state = State.objects.get(type_id="draft", slug="rfc") + rfc_published_state = State.objects.get(type_id="rfc", slug="published") + SearchRule.objects.filter(state=rfc_published_state).update(state=draft_rfc_state) + class Migration(migrations.Migration): dependencies = [ ("community", "0002_auto_20230320_1222"), ("doc", "0010_move_rfc_docaliases"), ] - - operations = [migrations.RunPython(forward)] + + operations = [migrations.RunPython(forward, reverse)] From e42ac40ec4902f4ff4b859b3d1d06c746c4a9991 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 11 Jul 2023 16:16:05 -0300 Subject: [PATCH 03/13] fix: Use (rfc, published) state for RFC SearchRules --- ietf/community/forms.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ietf/community/forms.py b/ietf/community/forms.py index 8d72ce0d7..fec53a178 100644 --- a/ietf/community/forms.py +++ b/ietf/community/forms.py @@ -42,7 +42,10 @@ class SearchRuleForm(forms.ModelForm): if rule_type == "group_exp": restrict_state("draft", "expired") else: - restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active") + if rule_type.endswith("rfc"): + restrict_state("rfc", "published") + else: + restrict_state("draft", "active") if rule_type.startswith("area"): self.fields["group"].label = "Area" @@ -70,7 +73,10 @@ class SearchRuleForm(forms.ModelForm): del self.fields["text"] elif rule_type in ["author", "author_rfc", "shepherd", "ad"]: - restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active") + if rule_type.endswith("rfc"): + restrict_state("rfc", "published") + else: + restrict_state("draft", "active") if rule_type.startswith("author"): self.fields["person"].label = "Author" @@ -84,7 +90,10 @@ class SearchRuleForm(forms.ModelForm): del self.fields["text"] elif rule_type == "name_contains": - restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active") + if rule_type.endswith("rfc"): + restrict_state("rfc", "published") + else: + restrict_state("draft", "active") del self.fields["person"] del self.fields["group"] From 7d5b1767f566b6ec00f4ceb6686ecf7001e933cc Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 11 Jul 2023 16:35:25 -0300 Subject: [PATCH 04/13] fix: Fix display of RFCs in search_result_row.html n.b., dropped the "(was draft...)" suffix for now - need to revive that or commit to removing it. --- ietf/templates/doc/search/search_result_row.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ietf/templates/doc/search/search_result_row.html b/ietf/templates/doc/search/search_result_row.html index d47dd4389..422ef4b0a 100644 --- a/ietf/templates/doc/search/search_result_row.html +++ b/ietf/templates/doc/search/search_result_row.html @@ -49,13 +49,13 @@ {% if doc.pages %}{{ doc.pages }} page{{ doc.pages|pluralize }}{% endif %}
- {% if doc.get_state_slug == "rfc" %} + {% if doc.type_id == "rfc" %} RFC {{ doc.rfc_number }} {% else %} {{ doc.name }}-{{ doc.rev }} {% endif %} - {% if doc.get_state_slug == "rfc" and "draft" in doc.name %}(was {{ doc.name }}){% endif %} +{# {% if doc.type_id == "rfc" and "draft" in doc.name %}(was {{ doc.name }}){% endif %} TODO drop this or look up the corresponding draft name#}
{% comment %}
@@ -106,19 +106,19 @@ {% endif %} - {% if doc.latest_revision_date|timesince_days|new_enough:request and doc.get_state_slug != "rfc" %} + {% if doc.latest_revision_date|timesince_days|new_enough:request and doc.type_id != "rfc" %} {% if doc.rev != "00" %} {% elif doc.replaces %} {% endif %} {% endif %} - {% if doc.get_state_slug == "rfc" %} + {% if doc.type_id == "rfc" %} {{ doc.latest_revision_date|date:"Y-m" }} {% else %} {{ doc.latest_revision_date|date:"Y-m-d" }} {% endif %} - {% if doc.latest_revision_date|timesince_days|new_enough:request and doc.get_state_slug != "rfc" %} + {% if doc.latest_revision_date|timesince_days|new_enough:request and doc.type_id != "rfc" %} {% if doc.rev != "00" or doc.replaces %}{% endif %} {% endif %} {% if doc.latest_revision_date|timesince_days|new_enough:request %} @@ -127,7 +127,7 @@ New
{% endif %} - {% if doc.get_state_slug == "active" and doc.expirable and doc.expires|timesince_days|expires_soon:request %} + {% if doc.type_id == "draft" and doc.get_state_slug == "active" and doc.expirable and doc.expires|timesince_days|expires_soon:request %}
Expires soon {% endif %} From 1660a1433100c8c49c29ffc573c530b76b11309e Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 21 Jul 2023 13:33:33 -0300 Subject: [PATCH 05/13] test: Use (rfc, published) state in test --- ietf/community/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/community/tests.py b/ietf/community/tests.py index 3dd86f70e..19530efcd 100644 --- a/ietf/community/tests.py +++ b/ietf/community/tests.py @@ -151,7 +151,7 @@ class CommunityListTests(WebTest): "action": "add_rule", "rule_type": "author_rfc", "author_rfc-person": Person.objects.filter(documentauthor__document=draft).first().pk, - "author_rfc-state": State.objects.get(type="draft", slug="rfc").pk, + "author_rfc-state": State.objects.get(type="rfc", slug="published").pk, }) self.assertEqual(r.status_code, 302) clist = CommunityList.objects.get(user__username="plain") @@ -408,4 +408,4 @@ class CommunityListTests(WebTest): self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue(draft.name in outbox[-1]["Subject"]) - \ No newline at end of file + From 0959a2e82e734509b97354ade8d0c45476db8a49 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Sat, 22 Jul 2023 11:13:36 -0700 Subject: [PATCH 06/13] refactor: Filter SearchRule matching using rfc doc type --- ietf/community/utils.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ietf/community/utils.py b/ietf/community/utils.py index 8130954b9..71905af93 100644 --- a/ietf/community/utils.py +++ b/ietf/community/utils.py @@ -71,25 +71,33 @@ def update_name_contains_indexes_with_new_doc(doc): if re.search(r.text, doc.name) and not doc in r.name_contains_index.all(): r.name_contains_index.add(doc) + def docs_matching_community_list_rule(rule): docs = Document.objects.all() + + if rule.rule_type.endswith("_rfc"): + docs = docs.filter(type_id="rfc") # rule.state is ignored for RFCs + else: + docs = docs.filter(type_id="draft", states=rule.state) + 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) + return docs.filter(Q(group=rule.group_id) | Q(group__parent=rule.group_id)) elif rule.rule_type in ['group_exp']: - return docs.filter(group=rule.group_id, states=rule.state) + return docs.filter(group=rule.group_id) elif rule.rule_type.startswith("state_"): - return docs.filter(states=rule.state) + return docs elif rule.rule_type in ["author", "author_rfc"]: - return docs.filter(states=rule.state, documentauthor__person=rule.person) + return docs.filter(documentauthor__person=rule.person) elif rule.rule_type == "ad": - return docs.filter(states=rule.state, ad=rule.person) + return docs.filter(ad=rule.person) elif rule.rule_type == "shepherd": - return docs.filter(states=rule.state, shepherd__person=rule.person) + return docs.filter(shepherd__person=rule.person) elif rule.rule_type == "name_contains": - return docs.filter(states=rule.state, searchrule=rule) + return docs.filter(searchrule=rule) raise NotImplementedError + def community_list_rules_matching_doc(doc): states = list(doc.states.values_list("pk", flat=True)) From 2f94383fca67e50c3ed942e94a9b6d55da933855 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Sat, 22 Jul 2023 11:29:18 -0700 Subject: [PATCH 07/13] refactor: Find SearchRules using rfc doc type --- ietf/community/utils.py | 83 +++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 29 deletions(-) diff --git a/ietf/community/utils.py b/ietf/community/utils.py index 71905af93..ba4cc67fe 100644 --- a/ietf/community/utils.py +++ b/ietf/community/utils.py @@ -99,50 +99,75 @@ def docs_matching_community_list_rule(rule): def community_list_rules_matching_doc(doc): + rules = SearchRule.objects.none() + if doc.type_id not in ["draft", "rfc"]: + return rules # none states = list(doc.states.values_list("pk", flat=True)) - rules = SearchRule.objects.none() - + # group and area rules if doc.group_id: groups = [doc.group_id] if doc.group.parent_id: groups.append(doc.group.parent_id) + rules_to_add = SearchRule.objects.filter(group__in=groups) + if doc.type_id == "rfc": + rules_to_add = rules_to_add.filter(rule_type__in=["group_rfc", "area_rfc"]) + else: + rules_to_add = rules_to_add.filter( + rule_type__in=["group", "area", "group_exp"], + state__in=states, + ) + rules |= rules_to_add + + # state rules (only relevant for I-Ds) + if doc.type_id == "draft": rules |= SearchRule.objects.filter( - rule_type__in=['group', 'area', 'group_rfc', 'area_rfc', 'group_exp'], + rule_type__in=[ + "state_iab", + "state_iana", + "state_iesg", + "state_irtf", + "state_ise", + "state_rfceditor", + "state_ietf", + ], state__in=states, - group__in=groups ) - rules |= SearchRule.objects.filter( - rule_type__in=['state_iab', 'state_iana', 'state_iesg', 'state_irtf', 'state_ise', 'state_rfceditor', 'state_ietf'], - state__in=states, - ) - - rules |= SearchRule.objects.filter( - rule_type__in=["author", "author_rfc"], - state__in=states, - person__in=list(Person.objects.filter(documentauthor__document=doc)), - ) - - if doc.ad_id: + # author rules + if doc.type_id == "rfc": rules |= SearchRule.objects.filter( - rule_type="ad", + rule_type="author_rfc", + person__in=list(Person.objects.filter(documentauthor__document=doc)), + ) + else: + rules |= SearchRule.objects.filter( + rule_type="author", state__in=states, - person=doc.ad_id, + person__in=list(Person.objects.filter(documentauthor__document=doc)), ) - if doc.shepherd_id: - rules |= SearchRule.objects.filter( - rule_type="shepherd", - state__in=states, - person__email=doc.shepherd_id, - ) + # Other draft-only rules rules + if doc.type_id == "draft": + if doc.ad_id: + rules |= SearchRule.objects.filter( + rule_type="ad", + state__in=states, + person=doc.ad_id, + ) - rules |= SearchRule.objects.filter( - rule_type="name_contains", - state__in=states, - name_contains_index=doc, # search our materialized index to avoid full scan - ) + if doc.shepherd_id: + rules |= SearchRule.objects.filter( + rule_type="shepherd", + state__in=states, + person__email=doc.shepherd_id, + ) + + rules |= SearchRule.objects.filter( + rule_type="name_contains", + state__in=states, + name_contains_index=doc, # search our materialized index to avoid full scan + ) return rules From a6d5cb69d46c6e9ecf8525f731abbca7a5147f7c Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Sat, 22 Jul 2023 11:39:34 -0700 Subject: [PATCH 08/13] refactor: Use empty rule.states for _rfc rules --- ietf/community/forms.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ietf/community/forms.py b/ietf/community/forms.py index fec53a178..100f9a08d 100644 --- a/ietf/community/forms.py +++ b/ietf/community/forms.py @@ -9,6 +9,7 @@ from django.db.models import Q from ietf.community.models import SearchRule, EmailSubscription from ietf.doc.fields import SearchableDocumentsField +from ietf.doc.models import State from ietf.person.models import Person from ietf.person.fields import SearchablePersonField @@ -38,13 +39,16 @@ class SearchRuleForm(forms.ModelForm): f.initial = f.queryset[0].pk f.widget = forms.HiddenInput() + if rule_type.endswith("_rfc"): + self.fields["state"].queryset = State.objects.none() + self.fields["state"].initial = None + self.fields["state"].widget = forms.HiddenInput() + if rule_type in ["group", "group_rfc", "area", "area_rfc", "group_exp"]: if rule_type == "group_exp": restrict_state("draft", "expired") else: - if rule_type.endswith("rfc"): - restrict_state("rfc", "published") - else: + if not rule_type.endswith("_rfc"): restrict_state("draft", "active") if rule_type.startswith("area"): @@ -73,9 +77,7 @@ class SearchRuleForm(forms.ModelForm): del self.fields["text"] elif rule_type in ["author", "author_rfc", "shepherd", "ad"]: - if rule_type.endswith("rfc"): - restrict_state("rfc", "published") - else: + if not rule_type.endswith("_rfc"): restrict_state("draft", "active") if rule_type.startswith("author"): @@ -90,9 +92,7 @@ class SearchRuleForm(forms.ModelForm): del self.fields["text"] elif rule_type == "name_contains": - if rule_type.endswith("rfc"): - restrict_state("rfc", "published") - else: + if not rule_type.endswith("_rfc"): restrict_state("draft", "active") del self.fields["person"] From 8f738493437f57e4a45c30a6bd748629da50b14a Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Sat, 22 Jul 2023 11:51:27 -0700 Subject: [PATCH 09/13] refactor: Remove "state" field entirely for rfc rules --- ietf/community/forms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ietf/community/forms.py b/ietf/community/forms.py index 100f9a08d..a2bf96dab 100644 --- a/ietf/community/forms.py +++ b/ietf/community/forms.py @@ -31,6 +31,8 @@ class SearchRuleForm(forms.ModelForm): super(SearchRuleForm, self).__init__(*args, **kwargs) def restrict_state(state_type, slug=None): + if "state" not in self.fields: + raise RuntimeError(f"Rule type {rule_type} cannot include state filtering") f = self.fields['state'] f.queryset = f.queryset.filter(used=True).filter(type=state_type) if slug: @@ -40,9 +42,7 @@ class SearchRuleForm(forms.ModelForm): f.widget = forms.HiddenInput() if rule_type.endswith("_rfc"): - self.fields["state"].queryset = State.objects.none() - self.fields["state"].initial = None - self.fields["state"].widget = forms.HiddenInput() + del self.fields["state"] # rfc rules must not look at document states if rule_type in ["group", "group_rfc", "area", "area_rfc", "group_exp"]: if rule_type == "group_exp": From 4e670692f8c3e8758860d8a436a90692b241ced2 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Sat, 22 Jul 2023 13:29:43 -0700 Subject: [PATCH 10/13] refactor: Clear rule.state when migrating *_rfc rules --- ietf/community/migrations/0003_track_rfcs.py | 23 +++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/ietf/community/migrations/0003_track_rfcs.py b/ietf/community/migrations/0003_track_rfcs.py index 77a553ec1..2bf90fdb0 100644 --- a/ietf/community/migrations/0003_track_rfcs.py +++ b/ietf/community/migrations/0003_track_rfcs.py @@ -18,24 +18,27 @@ def forward(apps, schema_editor): ): cl.added_docs.add(rfc) - # Handle rules + # Handle rules - rules ending with _rfc should no longer filter by state. + # There are 9 CommunityLists with invalid author_rfc rules that are filtering + # by (draft, active) instead of (draft, rfc) state before migration. All but one + # also includes an author rule for (draft, active), so these will start following + # RFCs as well. The one exception will start tracking RFCs instead of I-Ds, which + # is probably what was intended, but will be a change in their user experience. SearchRule = apps.get_model("community", "SearchRule") - State = apps.get_model("doc", "State") - draft_rfc_state = State.objects.get(type_id="draft", slug="rfc") - rfc_published_state = State.objects.get(type_id="rfc", slug="published") - SearchRule.objects.filter(state=draft_rfc_state).update(state=rfc_published_state) - + rfc_rules = SearchRule.objects.filter(rule_type__endswith="_rfc") + rfc_rules.update(state=None) def reverse(apps, schema_editor): Document = apps.get_model("doc", "Document") for rfc in Document.objects.filter(type__slug="rfc"): rfc.communitylist_set.clear() + # See the comment above regarding author_rfc SearchRule = apps.get_model("community", "SearchRule") State = apps.get_model("doc", "State") - draft_rfc_state = State.objects.get(type_id="draft", slug="rfc") - rfc_published_state = State.objects.get(type_id="rfc", slug="published") - SearchRule.objects.filter(state=rfc_published_state).update(state=draft_rfc_state) + SearchRule.objects.filter(rule_type__endswith="_rfc").update( + state=State.objects.get(type_id="draft", slug="rfc") + ) class Migration(migrations.Migration): @@ -43,5 +46,5 @@ class Migration(migrations.Migration): ("community", "0002_auto_20230320_1222"), ("doc", "0010_move_rfc_docaliases"), ] - + operations = [migrations.RunPython(forward, reverse)] From 938f862cc299ef3d20d0cc591a067fc8b4845554 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Sat, 22 Jul 2023 14:11:57 -0700 Subject: [PATCH 11/13] refactor: Prevent undefined variable reference --- ietf/community/views.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ietf/community/views.py b/ietf/community/views.py index 054bed302..f1d353741 100644 --- a/ietf/community/views.py +++ b/ietf/community/views.py @@ -79,19 +79,18 @@ def manage_list(request, username=None, acronym=None, group_type=None): rule_type_form = SearchRuleTypeForm(request.POST) if rule_type_form.is_valid(): rule_type = rule_type_form.cleaned_data['rule_type'] - - if rule_type: - rule_form = SearchRuleForm(clist, rule_type, request.POST) - if rule_form.is_valid(): - if clist.pk is None: - clist.save() - - rule = rule_form.save(commit=False) - rule.community_list = clist - rule.rule_type = rule_type - rule.save() - if rule.rule_type == "name_contains": - reset_name_contains_index_for_rule(rule) + if rule_type: + rule_form = SearchRuleForm(clist, rule_type, request.POST) + if rule_form.is_valid(): + if clist.pk is None: + clist.save() + + rule = rule_form.save(commit=False) + rule.community_list = clist + rule.rule_type = rule_type + rule.save() + if rule.rule_type == "name_contains": + reset_name_contains_index_for_rule(rule) return HttpResponseRedirect("") else: From 423514467dccb7d5aed3654a1f8548c6aecc716f Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Sat, 22 Jul 2023 14:23:44 -0700 Subject: [PATCH 12/13] fix: Track RFC documents that came from tracked drafts --- ietf/community/utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ietf/community/utils.py b/ietf/community/utils.py index ba4cc67fe..4a6a2fadd 100644 --- a/ietf/community/utils.py +++ b/ietf/community/utils.py @@ -179,7 +179,11 @@ def docs_tracked_by_community_list(clist): # in theory, we could use an OR query, but databases seem to have # trouble with OR queries and complicated joins so do the OR'ing # manually - doc_ids = set(clist.added_docs.values_list("pk", flat=True)) + doc_ids = set() + for doc in clist.added_docs.all(): + doc_ids.add(doc.pk) + doc_ids.update(alias.docs.first().pk for alias in doc.related_that_doc("became_rfc")) + for rule in clist.searchrule_set.all(): doc_ids = doc_ids | set(docs_matching_community_list_rule(rule).values_list("pk", flat=True)) From 2b0f95477eca43b9f91980445a24977b705cf7ff Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Sat, 22 Jul 2023 15:45:25 -0700 Subject: [PATCH 13/13] test: Remove unused import --- ietf/community/forms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ietf/community/forms.py b/ietf/community/forms.py index a2bf96dab..ad8570896 100644 --- a/ietf/community/forms.py +++ b/ietf/community/forms.py @@ -9,7 +9,6 @@ from django.db.models import Q from ietf.community.models import SearchRule, EmailSubscription from ietf.doc.fields import SearchableDocumentsField -from ietf.doc.models import State from ietf.person.models import Person from ietf.person.fields import SearchablePersonField