コード例 #1
0
    async def _heatmap(
        self,
        ctx: SlashContext,
        username: Optional[str] = "me",
        after: Optional[str] = None,
        before: Optional[str] = None,
    ) -> None:
        """Generate a heatmap for the given user."""
        start = datetime.now()

        after_time, before_time, time_str = parse_time_constraints(
            after, before)

        msg = await ctx.send(i18n["heatmap"]["getting_heatmap"].format(
            user=get_initial_username(username, ctx), time_str=time_str))

        utc_offset = extract_utc_offset(ctx.author.display_name)

        from_str = after_time.isoformat() if after_time else None
        until_str = before_time.isoformat() if before_time else None

        user = get_user(username, ctx, self.blossom_api)

        heatmap_response = self.blossom_api.get(
            "submission/heatmap/",
            params={
                "completed_by": get_user_id(user),
                "utc_offset": utc_offset,
                "complete_time__gte": from_str,
                "complete_time__lte": until_str,
            },
        )
        if heatmap_response.status_code != 200:
            raise BlossomException(heatmap_response)

        data = heatmap_response.json()

        day_index = pd.Index(range(1, 8))
        hour_index = pd.Index(range(0, 24))

        heatmap = (
            # Create a data frame from the data
            pd.DataFrame.from_records(data, columns=["day", "hour", "count"])
            # Convert it into a table with the days as rows and hours as columns
            .pivot(index="day", columns="hour", values="count")
            # Add the missing days and hours
            .reindex(index=day_index, columns=hour_index))

        heatmap_table = create_file_from_heatmap(heatmap, user, utc_offset)

        await msg.edit(
            content=i18n["heatmap"]["response_message"].format(
                user=get_username(user),
                time_str=time_str,
                duration=get_duration_str(start),
            ),
            file=heatmap_table,
        )
コード例 #2
0
    async def search(
        self,
        ctx: SlashContext,
        query: str,
        username: str = "me",
        after: Optional[str] = None,
        before: Optional[str] = None,
    ) -> None:
        """Search for transcriptions containing the given text."""
        start = datetime.now()
        after_time, before_time, time_str = parse_time_constraints(
            after, before)

        # Send a first message to show that the bot is responsive.
        # We will edit this message later with the actual content.
        msg = await ctx.send(i18n["search"]["getting_search"].format(
            query=query,
            user=get_initial_username(username, ctx),
            time_str=time_str))

        user = get_user(username, ctx, self.blossom_api)

        # Simulate an initial cache item
        cache_item: SearchCacheItem = {
            "query": query,
            "user": user,
            "after_time": after_time,
            "before_time": before_time,
            "time_str": time_str,
            "cur_page": 0,
            "discord_user_id": ctx.author_id,
            "response_data": None,
            "request_page": 0,
        }

        # Display the first page
        await self._search_from_cache(msg, start, cache_item, 0)
コード例 #3
0
ファイル: leaderboard.py プロジェクト: GrafeasGroup/buttercup
    async def leaderboard(
        self,
        ctx: SlashContext,
        username: str = "me",
        after: Optional[str] = None,
        before: Optional[str] = None,
    ) -> None:
        """Get the leaderboard for the given user."""
        start = datetime.now(tz=pytz.utc)

        after_time, before_time, time_str = parse_time_constraints(after, before)

        # Send a first message to show that the bot is responsive.
        # We will edit this message later with the actual content.
        msg = await ctx.send(
            i18n["leaderboard"]["getting_leaderboard"].format(
                user=get_initial_username(username, ctx), time_str=time_str
            )
        )

        user = get_user(username, ctx, self.blossom_api)

        top_count = 5 if user else 15
        context_count = 5

        from_str = after_time.isoformat() if after_time else None
        until_str = before_time.isoformat() if before_time else None

        # Get the leaderboard data
        leaderboard_response = self.blossom_api.get(
            "submission/leaderboard",
            params={
                "user_id": get_user_id(user),
                "top_count": top_count,
                "below_count": context_count,
                "above_count": context_count,
                "complete_time__gte": from_str,
                "complete_time__lte": until_str,
            },
        )
        if leaderboard_response.status_code != 200:
            raise BlossomException(leaderboard_response)

        leaderboard = leaderboard_response.json()
        # Extract needed data
        top_users = leaderboard["top"]
        above_users = leaderboard["above"]
        lb_user = leaderboard["user"]
        below_users = leaderboard["below"]

        description = ""

        # Only show the top users if they are not already included
        top_user_limit = (
            top_count + 1
            if user is None
            else above_users[0]["rank"]
            if len(above_users) > 0
            else lb_user["rank"]
        )

        # Show top users
        for top_user in top_users[: top_user_limit - 1]:
            description += format_leaderboard_user(top_user) + "\n"

        rank = get_rank(top_users[0]["gamma"])

        if user:
            # Add separator if necessary
            if top_user_limit > top_count + 1:
                description += "...\n"

            # Show users with more gamma than the current user
            for above_user in above_users:
                description += format_leaderboard_user(above_user) + "\n"

            # Show the current user
            description += "**" + format_leaderboard_user(lb_user) + "**\n"

            # Show users with less gamma than the current user
            for below_user in below_users:
                description += format_leaderboard_user(below_user) + "\n"

            rank = get_rank(user["gamma"])

        time_frame = format_leaderboard_timeframe(after_time, before_time)

        await msg.edit(
            content=i18n["leaderboard"]["embed_message"].format(
                user=get_username(user),
                time_str=time_str,
                duration=get_duration_str(start),
            ),
            embed=Embed(
                title=i18n["leaderboard"]["embed_title"].format(
                    user=get_username(user), time_frame=time_frame
                ),
                description=description,
                color=Colour.from_rgb(*get_rgb_from_hex(rank["color"])),
            ),
        )
コード例 #4
0
ファイル: history.py プロジェクト: GrafeasGroup/buttercup
    async def _until(
        self,
        ctx: SlashContext,
        goal: Optional[str] = None,
        username: str = "me",
        after: str = "1 week",
        before: Optional[str] = None,
    ) -> None:
        """Determine how long it will take the user to reach the given goal."""
        start = datetime.now(tz=pytz.utc)

        after_time, before_time, time_str = parse_time_constraints(
            after, before)

        if not after_time:
            # We need a starting point for the calculations
            raise InvalidArgumentException("after", after)

        # Send a first message to show that the bot is responsive.
        # We will edit this message later with the actual content.
        msg = await ctx.send(i18n["until"]["getting_prediction"].format(
            user=get_initial_username(username, ctx),
            time_str=time_str,
        ))

        user = get_user(username, ctx, self.blossom_api)

        if goal is not None:
            try:
                # Check if the goal is a gamma value or rank name
                goal_gamma, goal_str = parse_goal_str(goal)
            except InvalidArgumentException:
                # The goal could be a username
                if not user:
                    # If the user is the combined server, a target user doesn't make sense
                    raise InvalidArgumentException("goal", goal)

                # Try to treat the goal as a user
                return await self._until_user_catch_up(
                    ctx,
                    msg,
                    user,
                    goal,
                    start,
                    after_time,
                    before_time,
                    time_str,
                )
        elif user:
            # Take the next rank for the user
            next_rank = get_next_rank(user["gamma"])
            if next_rank:
                goal_gamma, goal_str = parse_goal_str(next_rank["name"])
            else:
                # If the user has reached the maximum rank, take the next 10,000 tier
                goal_gamma = ((user["gamma"] + 10_000) // 10_000) * 10_000
                goal_str = f"{goal_gamma:,}"
        else:
            # You can't get the "next rank" of the whole server
            raise InvalidArgumentException("goal", "<empty>")

        user_gamma = get_user_gamma(user, self.blossom_api)

        await msg.edit(
            content=i18n["until"]["getting_prediction_to_goal"].format(
                user=get_username(user),
                goal=goal_str,
                time_str=time_str,
            ))

        description = await _get_progress_description(
            user,
            user_gamma,
            goal_gamma,
            goal_str,
            start,
            after_time,
            before_time,
            blossom_api=self.blossom_api,
        )

        # Determine the color of the target rank
        color = get_rank(goal_gamma)["color"]

        await msg.edit(
            content=i18n["until"]["embed_message"].format(
                user=get_username(user),
                goal=goal_str,
                time_str=time_str,
                duration=get_duration_str(start),
            ),
            embed=Embed(
                title=i18n["until"]["embed_title"].format(
                    user=get_username(user)),
                description=description,
                color=discord.Colour.from_rgb(*get_rgb_from_hex(color)),
            ),
        )
コード例 #5
0
ファイル: history.py プロジェクト: GrafeasGroup/buttercup
    async def rate(
        self,
        ctx: SlashContext,
        usernames: str = "me",
        after: Optional[str] = None,
        before: Optional[str] = None,
    ) -> None:
        """Get the transcription rate of the user."""
        start = datetime.now()

        after_time, before_time, time_str = parse_time_constraints(
            after, before)

        utc_offset = extract_utc_offset(ctx.author.display_name)

        # Give a quick response to let the user know we're working on it
        # We'll later edit this message with the actual content
        msg = await ctx.send(i18n["rate"]["getting_rate"].format(
            users=get_initial_username_list(usernames, ctx),
            time_str=time_str,
        ))

        users = get_user_list(usernames, ctx, self.blossom_api)
        if users:
            users.sort(key=lambda u: u["gamma"], reverse=True)
        colors = get_user_colors(users)

        max_rates = []

        fig: plt.Figure = plt.figure()
        ax: plt.Axes = fig.gca()

        fig.subplots_adjust(bottom=0.2)
        ax.set_xlabel(i18n["rate"]["plot_xlabel"].format(
            timezone=utc_offset_to_str(utc_offset)))
        ax.set_ylabel(i18n["rate"]["plot_ylabel"])

        for label in ax.get_xticklabels():
            label.set_rotation(32)
            label.set_ha("right")

        ax.set_title(i18n["rate"]["plot_title"].format(
            users=get_usernames(users, 2, escape=False)))

        for index, user in enumerate(users or [None]):
            if users and len(users) > 1:
                await msg.edit(content=i18n["rate"]["getting_rate"].format(
                    users=get_usernames(users),
                    count=index + 1,
                    total=len(users),
                    time_str=time_str,
                ))

            user_data = self.get_all_rate_data(user, "day", after_time,
                                               before_time, utc_offset)

            max_rate = user_data["count"].max()
            max_rates.append(max_rate)
            max_rate_point = user_data[user_data["count"] == max_rate].iloc[0]

            color = colors[index]

            # Plot the graph
            ax.plot(
                "date",
                "count",
                data=user_data.reset_index(),
                color=color,
            )
            # At a point for the max value
            ax.scatter(
                max_rate_point.name,
                max_rate_point.at["count"],
                color=color,
                s=4,
            )
            # Label the max value
            ax.annotate(
                int(max_rate_point.at["count"]),
                xy=(max_rate_point.name, max_rate_point.at["count"]),
                color=color,
            )

        if users:
            # A milestone at every 100 rate
            milestones = [
                dict(threshold=i * 100, color=ranks[i + 2]["color"])
                for i in range(1, 8)
            ]
            ax = add_milestone_lines(ax, milestones, 0, max(max_rates), 40)

        if users and len(users) > 1:
            ax.legend([get_username(user, escape=False) for user in users])

        discord_file = create_file_from_figure(fig, "rate_plot.png")

        await msg.edit(
            content=i18n["rate"]["response_message"].format(
                usernames=get_usernames(users),
                time_str=time_str,
                duration=get_duration_str(start),
            ),
            file=discord_file,
        )
コード例 #6
0
ファイル: history.py プロジェクト: GrafeasGroup/buttercup
    async def history(
        self,
        ctx: SlashContext,
        usernames: str = "me",
        after: Optional[str] = None,
        before: Optional[str] = None,
    ) -> None:
        """Get the transcription history of the user."""
        start = datetime.now()

        after_time, before_time, time_str = parse_time_constraints(
            after, before)

        utc_offset = extract_utc_offset(ctx.author.display_name)

        # Give a quick response to let the user know we're working on it
        # We'll later edit this message with the actual content
        msg = await ctx.send(i18n["history"]["getting_history"].format(
            users=get_initial_username_list(usernames, ctx),
            time_str=time_str,
        ))

        users = get_user_list(usernames, ctx, self.blossom_api)
        if users:
            users.sort(key=lambda u: u["gamma"], reverse=True)
        colors = get_user_colors(users)

        min_gammas = []
        max_gammas = []

        fig: plt.Figure = plt.figure()
        ax: plt.Axes = fig.gca()

        fig.subplots_adjust(bottom=0.2)
        ax.set_xlabel(i18n["history"]["plot_xlabel"].format(
            timezone=utc_offset_to_str(utc_offset)))
        ax.set_ylabel(i18n["history"]["plot_ylabel"])

        for label in ax.get_xticklabels():
            label.set_rotation(32)
            label.set_ha("right")

        ax.set_title(i18n["history"]["plot_title"].format(
            users=get_usernames(users, 2, escape=False)))

        for index, user in enumerate(users or [None]):
            if users and len(users) > 1:
                await msg.edit(
                    content=i18n["history"]["getting_history_progress"].format(
                        users=get_usernames(users),
                        time_str=time_str,
                        count=index + 1,
                        total=len(users),
                    ))

            history_data = self.get_user_history(user, after_time, before_time,
                                                 utc_offset)

            color = colors[index]
            first_point = history_data.iloc[0]
            last_point = history_data.iloc[-1]

            min_gammas.append(first_point.at["gamma"])
            max_gammas.append(last_point.at["gamma"])

            # Plot the graph
            ax.plot(
                "date",
                "gamma",
                data=history_data.reset_index(),
                color=color,
            )
            # At a point for the last value
            ax.scatter(
                last_point.name,
                last_point.at["gamma"],
                color=color,
                s=4,
            )
            # Label the last value
            ax.annotate(
                int(last_point.at["gamma"]),
                xy=(last_point.name, last_point.at["gamma"]),
                color=color,
            )

        if users:
            # Show milestone lines
            min_value, max_value = min(min_gammas), max(max_gammas)
            delta = (max_value - min_value) * 0.4
            ax = add_milestone_lines(ax, ranks, min_value, max_value, delta)

        if users and len(users) > 1:
            ax.legend([get_username(user, escape=False) for user in users])

        discord_file = create_file_from_figure(fig, "history_plot.png")

        await msg.edit(
            content=i18n["history"]["response_message"].format(
                users=get_usernames(users),
                time_str=time_str,
                duration=get_duration_str(start),
            ),
            file=discord_file,
        )