Ejemplo n.º 1
0
    async def _send_stats(self, ctx, preamble):
        placings = 5
        database_key = f"race.scores:{ctx.channel.id}"
        if database.zcard(database_key) == 0:
            logger.info(f"no users in {database_key}")
            await ctx.send("There are no users in the database.")
            return

        if placings > database.zcard(database_key):
            placings = database.zcard(database_key)

        leaderboard_list = database.zrevrangebyscore(
            database_key, "+inf", "-inf", 0, placings, True
        )
        embed = discord.Embed(
            type="rich", colour=discord.Color.blurple(), title=preamble
        )
        embed.set_author(name="Bird ID - An Ornithology Bot")
        leaderboard = ""

        for i, stats in enumerate(leaderboard_list):
            if ctx.guild is not None:
                user = await fetch_get_user(int(stats[0]), ctx=ctx, member=True)
            else:
                user = None

            if user is None:
                user = await fetch_get_user(int(stats[0]), ctx=ctx, member=False)
                if user is None:
                    user_info = "**Deleted**"
                else:
                    user_info = f"**{esc(user.name)}#{user.discriminator}**"
            else:
                user_info = f"**{esc(user.name)}#{user.discriminator}** ({user.mention})"

            leaderboard += f"{i+1}. {user_info} - {int(stats[1])}\n"

        start = int(database.hget(f"race.data:{ctx.channel.id}", "start"))
        elapsed = str(datetime.timedelta(seconds=round(time.time()) - start))

        embed.add_field(
            name="Options", value=await self._get_options(ctx), inline=False
        )
        embed.add_field(
            name="Stats", value=f"**Race Duration:** `{elapsed}`", inline=False
        )
        embed.add_field(name="Leaderboard", value=leaderboard, inline=False)

        if ctx.author:
            if database.zscore(database_key, str(ctx.author.id)) is not None:
                placement = int(database.zrevrank(database_key, str(ctx.author.id))) + 1
                embed.add_field(
                    name="You:", value=f"You are #{placement}.", inline=False
                )
            else:
                embed.add_field(
                    name="You:", value="You haven't answered any correctly."
                )

        await ctx.send(embed=embed)
Ejemplo n.º 2
0
    async def _send_stats(self, ctx, preamble):
        database_key = f"session.incorrect:{ctx.author.id}"

        embed = discord.Embed(type="rich",
                              colour=discord.Color.blurple(),
                              title=preamble)
        embed.set_author(name="Bird ID - An Ornithology Bot")

        if database.zcard(database_key) != 0:
            leaderboard_list = database.zrevrangebyscore(
                database_key, "+inf", "-inf", 0, 5, True)
            leaderboard = ""

            for i, stats in enumerate(leaderboard_list):
                leaderboard += (
                    f"{i+1}. **{stats[0].decode('utf-8')}** - {int(stats[1])}\n"
                )
        else:
            logger.info(f"no birds in {database_key}")
            leaderboard = "**There are no missed birds.**"

        embed.add_field(name="Options",
                        value=await self._get_options(ctx),
                        inline=False)
        embed.add_field(name="Stats",
                        value=await self._get_stats(ctx),
                        inline=False)
        embed.add_field(name="Top Missed Birds",
                        value=leaderboard,
                        inline=False)

        await ctx.send(embed=embed)
Ejemplo n.º 3
0
async def send_leaderboard(ctx, title, page, database_key=None, data=None):
    logger.info("building/sending leaderboard")

    if database_key is None and data is None:
        raise GenericError("database_key and data are both NoneType", 990)
    if database_key is not None and data is not None:
        raise GenericError("database_key and data are both set", 990)

    if page < 1:
        page = 1

    entry_count = (
        int(database.zcard(database_key)) if database_key is not None else data.count()
    )
    page = (page * 10) - 10

    if entry_count == 0:
        logger.info(f"no items in {database_key}")
        await ctx.send("There are no items in the database.")
        return

    if page > entry_count:
        page = entry_count - (entry_count % 10)

    items_per_page = 10
    leaderboard_list = (
        map(
            lambda x: (x[0].decode("utf-8"), x[1]),
            database.zrevrangebyscore(
                database_key, "+inf", "-inf", page, items_per_page, True
            ),
        )
        if database_key is not None
        else data.iloc[page : page + items_per_page - 1].items()
    )
    embed = discord.Embed(type="rich", colour=discord.Color.blurple())
    embed.set_author(name="Bird ID - An Ornithology Bot")
    leaderboard = ""

    for i, stats in enumerate(leaderboard_list):
        leaderboard += f"{i+1+page}. **{stats[0]}** - {int(stats[1])}\n"
    embed.add_field(name=title, value=leaderboard, inline=False)

    await ctx.send(embed=embed)
Ejemplo n.º 4
0
    async def user_lb(self, ctx, title, page, database_key=None, data=None):
        if database_key is None and data is None:
            raise GenericError("database_key and data are both NoneType", 990)
        if database_key is not None and data is not None:
            raise GenericError("database_key and data are both set", 990)

        if page < 1:
            page = 1

        user_amount = (
            int(database.zcard(database_key))
            if database_key is not None
            else data.count()
        )
        page = (page * 10) - 10

        if user_amount == 0:
            logger.info(f"no users in {database_key}")
            await ctx.send("There are no users in the database.")
            return

        if page >= user_amount:
            page = user_amount - (user_amount % 10 if user_amount % 10 != 0 else 10)

        users_per_page = 10
        leaderboard_list = (
            database.zrevrangebyscore(
                database_key, "+inf", "-inf", page, users_per_page, True
            )
            if database_key is not None
            else data.iloc[page : page + users_per_page - 1].items()
        )

        embed = discord.Embed(type="rich", colour=discord.Color.blurple())
        embed.set_author(name="Bird ID - An Ornithology Bot")
        leaderboard = ""

        for i, stats in enumerate(leaderboard_list):
            if ctx.guild is not None:
                user = ctx.guild.get_member(int(stats[0]))
            else:
                user = None

            if user is None:
                user = self.bot.get_user(int(stats[0]))
                if user is None:
                    user = "******"
                else:
                    user = f"**{user.name}#{user.discriminator}**"
            else:
                user = f"**{user.name}#{user.discriminator}** ({user.mention})"

            leaderboard += f"{i+1+page}. {user} - {int(stats[1])}\n"

        embed.add_field(name=title, value=leaderboard, inline=False)

        user_score = (
            database.zscore(database_key, str(ctx.author.id))
            if database_key is not None
            else data.get(str(ctx.author.id))
        )

        if user_score is not None:
            if database_key is not None:
                placement = int(database.zrevrank(database_key, str(ctx.author.id))) + 1
                distance = int(
                    database.zrevrange(
                        database_key, placement - 2, placement - 2, True
                    )[0][1]
                ) - int(user_score)
            else:
                placement = int(data.rank(ascending=False)[str(ctx.author.id)])
                distance = int(data.iloc[placement - 2] - user_score)

            if placement == 1:
                embed.add_field(
                    name="You:",
                    value=f"You are #{placement} on the leaderboard.\nYou are in first place.",
                    inline=False,
                )
            elif distance == 0:
                embed.add_field(
                    name="You:",
                    value=f"You are #{placement} on the leaderboard.\nYou are tied with #{placement-1}",
                    inline=False,
                )
            else:
                embed.add_field(
                    name="You:",
                    value=f"You are #{placement} on the leaderboard.\nYou are {distance} away from #{placement-1}",
                    inline=False,
                )
        else:
            embed.add_field(name="You:", value="You haven't answered any correctly.")

        await ctx.send(embed=embed)
Ejemplo n.º 5
0
    async def stats(self, ctx, topic="help"):
        logger.info("command: stats")

        if topic in ("scores", "score", "s"):
            topic = "scores"
        elif topic in ("usage", "u"):
            topic = "usage"
        elif topic in ("web", "w"):
            topic = "web"
        elif topic in ("help", ""):
            topic = "help"
        else:
            valid_topics = ("help", "scores", "usage", "web")
            await ctx.send(
                f"**`{topic}` is not a valid topic!**\nValid Topics: `{'`, `'.join(valid_topics)}`"
            )
            return

        embed = discord.Embed(
            title="Bot Stats",
            type="rich",
            color=discord.Color.blue(),
        )

        if topic == "help":
            embed.description = (
                "**Available statistic topics.**\n" +
                "This command is in progress and more stats may be added. " +
                "If there is a statistic you would like to see here, " +
                "please let us know in the support server.")
            embed.add_field(
                name="Scores",
                value=
                "`b!stats [scores|score|s]`\n*Displays stats about scores.*",
            ).add_field(
                name="Usage",
                value="`b!stats [usage|u]`\n*Displays stats about usage.*",
            ).add_field(
                name="Web",
                value="`b!stats [web|w]`\n*Displays stats about web usage.*",
            )

        elif topic == "scores":
            embed.description = "**Score Statistics**"
            scores = self.generate_series("users:global")
            scores = scores[scores > 0]
            c, d = np.histogram(scores,
                                bins=range(0, 1100, 100),
                                range=(0, 1000))
            c = (c / len(scores) * 100).round(1)
            embed.add_field(
                name="Totals",
                inline=False,
                value="**Sum of top 10 user scores:** `{:,}`\n".format(
                    scores.nlargest(n=10).sum()) +
                "**Sum of all positive user scores:** `{:,}`\n".format(
                    scores.sum()),
            ).add_field(
                name="Computations",
                inline=False,
                value="**Mean of all positive user scores:** `{:,.2f}`\n".
                format(scores.mean()) +
                "**Median of all positive user scores:** `{:,.1f}`\n".format(
                    scores.median()),
            ).add_field(
                name="Distributions",
                inline=False,
                value=
                f"**Number of users with scores over mean:** `{len(scores[scores > scores.mean()])}`\n"
                + "**Percentage of users with scores over mean:** `{:.1%}`".
                format(len(scores[scores > scores.mean()]) / len(scores)) +
                "\n**Percentage of users with scores between:**\n" + "".join(
                    f"\u2192 *{d[i]}-{d[i+1]-1}*: `{c[i]}%`\n"  # \u2192 is the "Rightwards Arrow"
                    for i in range(len(c))),
            )

        elif topic == "usage":
            embed.description = "**Usage Statistics**"

            today = datetime.datetime.now(datetime.timezone.utc).date()
            past_month = pd.date_range(  # pylint: disable=no-member
                today - datetime.timedelta(29), today).date
            keys = list(f"daily.score:{str(date)}" for date in past_month)
            keys = ["users:global"] + keys
            titles = reversed(range(
                1, 32))  # label columns by # days ago, today is 1 day ago
            month = self.generate_dataframe(keys, titles)
            total = month.loc[:, 31]
            month = month.loc[:, 30:1]  # remove totals column
            month = month.loc[(month != 0).any(1)]  # remove users with all 0s
            week = month.loc[:, 7:1]  # generate week from month
            week = week.loc[(week != 0).any(1)]
            today = week.loc[:, 1]  # generate today from week
            today = today.loc[today != 0]

            channels_see = len(list(self.bot.get_all_channels()))
            channels_used = int(database.zcard("score:global"))

            embed.add_field(
                name="Today (Since midnight UTC)",
                inline=False,
                value="**Accounts that answered at least 1 correctly:** `{:,}`\n"
                .format(len(today)) +
                "**Total birds answered correctly:** `{:,}`\n".format(
                    today.sum()),
            ).add_field(
                name="Last 7 Days",
                inline=False,
                value="**Accounts that answered at least 1 correctly:** `{:,}`\n"
                .format(len(week)) +
                "**Total birds answered correctly:** `{:,}`\n".format(
                    week.sum().sum()),
            ).add_field(
                name="Last 30 Days",
                inline=False,
                value="**Accounts that answered at least 1 correctly:** `{:,}`\n"
                .format(len(month)) +
                "**Total birds answered correctly:** `{:,}`\n".format(
                    month.sum().sum()),
            ).add_field(
                name="Total",
                inline=False,
                value="**Channels the bot can see:** `{:,}`\n".format(
                    channels_see) +
                "**Channels that have used the bot at least once:** `{:,} ({:,.1%})`\n"
                .format(channels_used, channels_used / channels_see) +
                "*(Note: Deleted channels or channels that the bot can't see anymore are still counted).*\n"
                +
                "**Accounts that have used any command at least once:** `{:,}`\n"
                .format(len(total)) +
                "**Accounts that answered at least 1 correctly:** `{:,} ({:,.1%})`\n"
                .format(len(total[total > 0]),
                        len(total[total > 0]) / len(total)),
            )

        elif topic == "web":
            embed.description = "**Web Usage Statistics**"

            today = datetime.datetime.now(datetime.timezone.utc).date()
            past_month = pd.date_range(  # pylint: disable=no-member
                today - datetime.timedelta(29), today).date
            web_score = (f"daily.webscore:{str(date)}" for date in past_month)
            web_usage = (f"daily.web:{str(date)}" for date in past_month)
            titles = tuple(reversed(range(
                1, 31)))  # label columns by # days ago, today is 1 day ago

            web_score_month = self.generate_dataframe(web_score, titles)
            web_score_week = web_score_month.loc[:, 7:
                                                 1]  # generate week from month
            web_score_week = web_score_week.loc[(
                web_score_week !=
                0).any(1)]  # remove users with no correct answers
            web_score_today = web_score_week.loc[:,
                                                 1]  # generate today from week
            web_score_today = web_score_today.loc[
                web_score_today != 0]  # remove users with no correct answers

            web_usage_month = self.generate_dataframe(web_usage,
                                                      titles,
                                                      index=("check", "skip",
                                                             "hint"))
            web_usage_week = web_usage_month.loc[:, 7:1]
            web_usage_today = web_usage_week.loc[:, 1]

            score_totals_keys = sorted(
                map(
                    lambda x: x.decode("utf-8"),
                    database.scan_iter(match="daily.webscore:????-??-??",
                                       count=5000),
                ))
            score_totals_titles = map(lambda x: x.split(":")[1],
                                      score_totals_keys)
            web_score_total = self.generate_dataframe(score_totals_keys,
                                                      score_totals_titles)

            usage_totals_keys = sorted(
                map(
                    lambda x: x.decode("utf-8"),
                    database.scan_iter(match="daily.web:????-??-??",
                                       count=5000),
                ))
            usage_totals_titles = map(lambda x: x.split(":")[1],
                                      usage_totals_keys)
            web_usage_total = self.generate_dataframe(usage_totals_keys,
                                                      usage_totals_titles,
                                                      index=("check", "skip",
                                                             "hint"))

            embed.add_field(
                name="Today (Since midnight UTC)",
                inline=False,
                value="**Accounts that answered at least 1 correctly:** `{:,}`\n"
                .format(len(web_score_today)) +
                "**Total birds answered correctly:** `{:,}`\n".format(
                    web_score_today.sum()) +
                "**Check command usage:** `{:,}`\n".format(
                    web_usage_today.loc["check"]) +
                "**Skip command usage:** `{:,}`\n".format(
                    web_usage_today.loc["skip"]) +
                "**Hint command usage:** `{:,}`\n".format(
                    web_usage_today.loc["hint"]),
            ).add_field(
                name="Last 7 Days",
                inline=False,
                value="**Accounts that answered at least 1 correctly:** `{:,}`\n"
                .format(len(web_score_week)) +
                "**Total birds answered correctly:** `{:,}`\n".format(
                    web_score_week.sum().sum()) +
                "**Check command usage:** `{:,}`\n".format(
                    web_usage_week.loc["check"].sum()) +
                "**Skip command usage:** `{:,}`\n".format(
                    web_usage_week.loc["skip"].sum()) +
                "**Hint command usage:** `{:,}`\n".format(
                    web_usage_week.loc["hint"].sum()),
            ).add_field(
                name="Last 30 Days",
                inline=False,
                value="**Accounts that answered at least 1 correctly:** `{:,}`\n"
                .format(len(web_score_month)) +
                "**Total birds answered correctly:** `{:,}`\n".format(
                    web_score_month.sum().sum()) +
                "**Check command usage:** `{:,}`\n".format(
                    web_usage_month.loc["check"].sum()) +
                "**Skip command usage:** `{:,}`\n".format(
                    web_usage_month.loc["skip"].sum()) +
                "**Hint command usage:** `{:,}`\n".format(
                    web_usage_month.loc["hint"].sum()),
            ).add_field(
                name="Total",
                inline=False,
                value="**Accounts that answered at least 1 correctly:** `{:,}`\n"
                .format(len(web_score_total)) +
                "**Total birds answered correctly:** `{:,}`\n".format(
                    web_score_total.sum().sum()) +
                "**Check command usage:** `{:,}`\n".format(
                    web_usage_total.loc["check"].sum()) +
                "**Skip command usage:** `{:,}`\n".format(
                    web_usage_total.loc["skip"].sum()) +
                "**Hint command usage:** `{:,}`\n".format(
                    web_usage_total.loc["hint"].sum()),
            )

        await ctx.send(embed=embed)
        return