diff options
| author | 2023-02-26 17:39:06 +0000 | |
|---|---|---|
| committer | 2023-02-26 17:39:06 +0000 | |
| commit | c391c79aafb39f21861a173284858e292bd5c80f (patch) | |
| tree | 344e42599e06e9dfe0c3c5b0b01ac3b5530981e2 | |
| parent | Add nominations api method to get activity (diff) | |
Only review users with recent activity
| -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."""  |