diff options
-rw-r--r-- | bot/exts/recruitment/talentpool/_review.py | 98 |
1 files changed, 66 insertions, 32 deletions
diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py index f41e08fe1..9436f2bad 100644 --- a/bot/exts/recruitment/talentpool/_review.py +++ b/bot/exts/recruitment/talentpool/_review.py @@ -37,6 +37,8 @@ MAX_ONGOING_REVIEWS = 3 MIN_REVIEW_INTERVAL = timedelta(days=1) # Minimum time between nomination and sending a review MIN_NOMINATION_TIME = timedelta(days=7) +# Number of days ago that the user must have activity since +RECENT_ACTIVITY_DAYS = 7 # A constant for weighting number of nomination entries against nomination age when selecting a user to review. # The higher this is, the lower the effect of review age. At 1, age and number of entries are weighted equally. @@ -107,56 +109,88 @@ class Reviewer: return True - async def get_nomination_to_review(self) -> Optional[Nomination]: + async def is_nomination_ready_for_review( + self, + nomination: Nomination, + user_message_count: int, + now: datetime, + ) -> bool: """ - Returns the Nomination of the next user to review, or None if there are no users ready. + Returns a boolean representing whether a nomination should be reviewed. Users will only be selected for review if: - They have not already been reviewed. - They have been nominated for longer than `MIN_NOMINATION_TIME`. + - They have sent at least one message in the server recently. + - They are still a member of the server. + """ + guild = self.bot.get_guild(Guild.id) + time_since_nomination = now - nomination.inserted_at + return ( + # Must be an active nomination + nomination.active and + # ... that has not already been reviewed + not nomination.reviewed and + # ... and has been nominated for long enough + time_since_nomination > MIN_NOMINATION_TIME and + # ... and is for a user that has been active recently + user_message_count > 0 and + # ... and is currently a member of the server + await get_or_fetch_member(guild, nomination.id) is not None + ) - The priority of the review is determined by how many nominations the user has - (more nominations = higher priority). - For users with equal priority the oldest nomination will be reviewed first. + async def sort_nominations_to_review(self, nominations: list[Nomination], now: datetime) -> list[Nomination]: """ - now = datetime.now(timezone.utc) + Sorts a list of nominations by priority for review. - possible_nominations: list[Nomination] = [] - nominations = await self.api.get_nominations(active=True) - for nomination in nominations: - time_since_nomination = now - nomination.inserted_at - if ( - not nomination.reviewed - and time_since_nomination > MIN_NOMINATION_TIME - ): - possible_nominations.append(nomination) + The priority of the review is determined based on how many nominations the user has + (more nominations = higher priority), and the age of the nomination. + """ + oldest_date = min(nomination.inserted_at for nomination in nominations) + max_entries = max(len(nomination.entries) for nomination in nominations) - if not possible_nominations: - log.debug("No users ready to review.") - return None + def score_nomination(nomination: Nomination) -> float: + """ + Scores a nomination based on age and number of nomination entries. - oldest_date = min(nomination.inserted_at for nomination in possible_nominations) - max_entries = max(len(nomination.entries) for nomination in possible_nominations) + The higher the score, the higher the priority for being put up for review should be. + """ + num_entries = len(nomination.entries) + entries_score = num_entries / max_entries - def sort_key(nomination: Nomination) -> float: - return self.score_nomination(nomination, oldest_date, now, max_entries) + nomination_date = nomination.inserted_at + age_score = (nomination_date - now) / (oldest_date - now) - return max(possible_nominations, key=sort_key) + return entries_score * REVIEW_SCORE_WEIGHT + age_score - @staticmethod - def score_nomination(nomination: Nomination, oldest_date: datetime, now: datetime, max_entries: int) -> float: + return sorted(nominations, key=score_nomination, reverse=True) + + async def get_nomination_to_review(self) -> Nomination | None: """ - Scores a nomination based on age and number of nomination entries. + Returns the Nomination of the next user to review, or None if there are no users ready. - The higher the score, the higher the priority for being put up for review should be. + See `is_ready_for_review` for the criteria for a user to be ready for review. + See `sort_nominations_to_review` for the criteria for a user to be prioritised for review. """ - num_entries = len(nomination.entries) - entries_score = num_entries / max_entries + now = datetime.now(timezone.utc) + nominations = await self.api.get_nominations(active=True) + if not nominations: + return None - nomination_date = nomination.inserted_at - age_score = (nomination_date - now) / (oldest_date - now) + messages_per_user = await self.api.get_activity( + [nomination.user_id for nomination in nominations], + days=RECENT_ACTIVITY_DAYS, + ) + possible_nominations = [ + nomination for nomination in nominations + if await self.is_nomination_ready_for_review(nomination, messages_per_user[nomination.user_id], now) + ] + if not possible_nominations: + log.info("No nominations are ready to review") + return None - return entries_score * REVIEW_SCORE_WEIGHT + age_score + sorted_nominations = await self.sort_nominations_to_review(possible_nominations, now) + return sorted_nominations[0] async def post_review(self, nomination: Nomination) -> None: """Format the review of a user and post it to the nomination voting channel.""" |