Skip to content

Commit 8bc4507

Browse files
feat: Give AD the action in ad-f-up doc state (ietf-tools#6272)
* refactor: Add helper class to compare tag changes * feat: Give AD the action in ad-f-up state * refactor: Remove unnecessary check * refactor: Reorganize update_action_holders() * refactor: Remove unnecessary guard * test: Update test for new AD handling * test: Update another test * test: Test ad-f-up effect on action holders --------- Co-authored-by: Robert Sparks <rjsparks@nostrum.com>
1 parent 18a1af2 commit 8bc4507

2 files changed

Lines changed: 71 additions & 23 deletions

File tree

ietf/doc/tests_utils.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,24 @@ def test_update_action_holders_resets_age(self):
154154
self.assertGreaterEqual(doc.documentactionholder_set.get(person=self.ad).time_added, right_now)
155155

156156
def test_update_action_holders_add_tag_need_rev(self):
157-
"""Adding need-rev tag adds authors as action holders"""
157+
"""Adding need-rev tag drops AD and adds authors as action holders"""
158158
doc = self.doc_in_iesg_state('pub-req')
159159
first_author = self.authors[0]
160160
doc.action_holders.add(first_author)
161+
doc.action_holders.add(doc.ad)
162+
self.assertCountEqual(doc.action_holders.all(), [first_author, doc.ad])
163+
self.update_doc_state(doc,
164+
doc.get_state('draft-iesg'),
165+
add_tags=['need-rev'],
166+
remove_tags=None)
167+
self.assertCountEqual(doc.action_holders.all(), self.authors)
168+
self.assertNotIn(self.ad, doc.action_holders.all())
169+
170+
# Check case where an author is ad
171+
doc = self.doc_in_iesg_state('pub-req')
172+
doc.ad = first_author
173+
doc.save()
174+
doc.action_holders.add(first_author)
161175
self.assertCountEqual(doc.action_holders.all(), [first_author])
162176
self.update_doc_state(doc,
163177
doc.get_state('draft-iesg'),
@@ -175,6 +189,12 @@ def test_update_action_holders_add_tag_need_rev_no_dups(self):
175189
remove_tags=None)
176190
self.assertCountEqual(doc.action_holders.all(), self.authors)
177191

192+
def test_update_action_holders_add_tag_ad_f_up(self):
193+
doc = self.doc_in_iesg_state('pub-req')
194+
self.assertEqual(doc.action_holders.count(), 0)
195+
self.update_doc_state(doc, doc.get_state('draft-iesg'), add_tags=['ad-f-up'])
196+
self.assertCountEqual(doc.action_holders.all(), [self.ad])
197+
178198
def test_update_action_holders_remove_tag_need_rev(self):
179199
"""Removing need-rev tag drops authors as action holders"""
180200
doc = self.doc_in_iesg_state('pub-req')
@@ -189,13 +209,14 @@ def test_update_action_holders_remove_tag_need_rev(self):
189209
def test_update_action_holders_add_tag_need_rev_ignores_non_authors(self):
190210
"""Adding need-rev tag does not affect existing action holders"""
191211
doc = self.doc_in_iesg_state('pub-req')
192-
doc.action_holders.add(self.ad)
193-
self.assertCountEqual(doc.action_holders.all(),[self.ad])
212+
other_person = PersonFactory()
213+
doc.action_holders.add(other_person)
214+
self.assertCountEqual(doc.action_holders.all(),[other_person])
194215
self.update_doc_state(doc,
195216
doc.get_state('draft-iesg'),
196217
add_tags=['need-rev'],
197218
remove_tags=None)
198-
self.assertCountEqual(doc.action_holders.all(), [self.ad] + self.authors)
219+
self.assertCountEqual(doc.action_holders.all(), [other_person] + self.authors)
199220

200221
def test_update_action_holders_remove_tag_need_rev_ignores_non_authors(self):
201222
"""Removing need-rev tag does not affect non-author action holders"""

ietf/doc/utils.py

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import textwrap
1313

1414
from collections import defaultdict, namedtuple, Counter
15+
from dataclasses import dataclass
1516
from typing import Union
1617
from zoneinfo import ZoneInfo
1718

@@ -460,6 +461,21 @@ def add_action_holder_change_event(doc, by, prev_set, reason=None):
460461
)
461462

462463

464+
@dataclass
465+
class TagSetComparer:
466+
before: set[str]
467+
after: set[str]
468+
469+
def changed(self):
470+
return self.before != self.after
471+
472+
def added(self, tag):
473+
return tag in self.after and tag not in self.before
474+
475+
def removed(self, tag):
476+
return tag in self.before and tag not in self.after
477+
478+
463479
def update_action_holders(doc, prev_state=None, new_state=None, prev_tags=None, new_tags=None):
464480
"""Update the action holders for doc based on state transition
465481
@@ -473,34 +489,45 @@ def update_action_holders(doc, prev_state=None, new_state=None, prev_tags=None,
473489
if prev_state and new_state:
474490
assert prev_state.type_id == new_state.type_id
475491

476-
# Convert tags to sets of slugs
477-
prev_tag_slugs = {t.slug for t in (prev_tags or [])}
478-
new_tag_slugs = {t.slug for t in (new_tags or [])}
492+
# Convert tags to sets of slugs
493+
tags = TagSetComparer(
494+
before={t.slug for t in (prev_tags or [])},
495+
after={t.slug for t in (new_tags or [])},
496+
)
479497

480498
# Do nothing if state / tag have not changed
481-
if (prev_state == new_state) and (prev_tag_slugs == new_tag_slugs):
499+
if (prev_state == new_state) and not tags.changed():
482500
return None
483501

484502
# Remember original list of action holders to later check if it changed
485503
prev_set = list(doc.action_holders.all())
486-
# Only draft-iesg states are of interest (for now)
487-
if (prev_state != new_state) and (getattr(new_state, 'type_id') == 'draft-iesg'):
504+
505+
# Update the action holders. To get this right for people with more
506+
# than one relationship to the document, do removals first, then adds.
507+
# Remove outdated action holders
508+
iesg_state_changed = (prev_state != new_state) and (getattr(new_state, "type_id", None) == "draft-iesg")
509+
if iesg_state_changed:
488510
# Clear the action_holders list on a state change. This will reset the age of any that get added back.
489511
doc.action_holders.clear()
490-
if doc.ad and new_state.slug not in DocumentActionHolder.CLEAR_ACTION_HOLDERS_STATES:
491-
# Default to responsible AD for states other than these
512+
if tags.removed("need-rev"):
513+
# Removed the 'need-rev' tag - drop authors from the action holders list
514+
DocumentActionHolder.objects.filter(document=doc, person__in=doc.authors()).delete()
515+
elif tags.added("need-rev"):
516+
# Remove the AD if we're asking for a new revision
517+
DocumentActionHolder.objects.filter(document=doc, person=doc.ad).delete()
518+
519+
# Add new action holders
520+
if doc.ad:
521+
# AD is an action holder unless specified otherwise for the new state
522+
if iesg_state_changed and new_state.slug not in DocumentActionHolder.CLEAR_ACTION_HOLDERS_STATES:
492523
doc.action_holders.add(doc.ad)
493-
494-
if prev_tag_slugs != new_tag_slugs:
495-
# If we have added or removed the need-rev tag, add or remove authors as action holders
496-
if ('need-rev' in prev_tag_slugs) and ('need-rev' not in new_tag_slugs):
497-
# Removed the 'need-rev' tag - drop authors from the action holders list
498-
DocumentActionHolder.objects.filter(document=doc, person__in=doc.authors()).delete()
499-
elif ('need-rev' not in prev_tag_slugs) and ('need-rev' in new_tag_slugs):
500-
# Added the 'need-rev' tag - add authors to the action holders list
501-
for auth in doc.authors():
502-
if not doc.action_holders.filter(pk=auth.pk).exists():
503-
doc.action_holders.add(auth)
524+
# If AD follow-up is needed, make sure they are an action holder
525+
if tags.added("ad-f-up"):
526+
doc.action_holders.add(doc.ad)
527+
# Authors get the action if a revision is needed
528+
if tags.added("need-rev"):
529+
for auth in doc.authors():
530+
doc.action_holders.add(auth)
504531

505532
# Now create an event if we changed the set
506533
return add_action_holder_change_event(

0 commit comments

Comments
 (0)