1212import textwrap
1313
1414from collections import defaultdict , namedtuple , Counter
15+ from dataclasses import dataclass
1516from typing import Union
1617from 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+
463479def 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