async def export(self, ctx): logger.info("command: export") files = [] async def _export_helper(database_keys, header: str, filename: str, users=False): if not isinstance(database_keys, str) and len(database_keys) > 1: data = self.generate_dataframe(database_keys, header.strip().split(",")[1:]) else: key = (database_keys if isinstance(database_keys, str) else database_keys[0]) data = self.generate_series(key) if users: data = await self.convert_users(data) with StringIO() as f: f.write(header) data.to_csv(f, header=False) with BytesIO(f.getvalue().encode("utf-8")) as b: files.append(discord.File(b, filename)) logger.info("exporting freq command") await _export_helper( "frequency.command:global", "command,amount used\n", "command_frequency.csv", users=False, ) logger.info("exporting freq bird") await _export_helper( "frequency.bird:global", "bird,amount seen\n", "bird_frequency.csv", users=False, ) logger.info("exporting streaks") await _export_helper( ["streak:global", "streak.max:global"], "username#discrim,current streak,max streak\n", "streaks.csv", True, ) logger.info("exporting missed") keys = sorted( map( lambda x: x.decode("utf-8"), database.scan_iter(match="daily.incorrect:????-??-??", count=5000), )) titles = ",".join(map(lambda x: x.split(":")[1], keys)) keys = ["incorrect:global"] + keys await _export_helper(keys, f"bird name,total missed,{titles}\n", "missed.csv", users=False) logger.info("exporting scores") keys = sorted( map( lambda x: x.decode("utf-8"), database.scan_iter(match="daily.score:????-??-??", count=5000), )) titles = ",".join(map(lambda x: x.split(":")[1], keys)) keys = ["users:global"] + keys await _export_helper(keys, f"username#discrim,total score,{titles}\n", "scores.csv", users=True) logger.info("exporting web scores") keys = sorted( map( lambda x: x.decode("utf-8"), database.scan_iter(match="daily.webscore:????-??-??", count=5000), )) titles = ",".join(map(lambda x: x.split(":")[1], keys)) await _export_helper(keys, f"username#discrim,{titles}\n", "web_scores.csv", users=True) logger.info("exporting web usage") keys = sorted( map( lambda x: x.decode("utf-8"), database.scan_iter(match="daily.web:????-??-??", count=5000), )) titles = ",".join(map(lambda x: x.split(":")[1], keys)) await _export_helper(keys, f"command,{titles}\n", "web_usage.csv", users=False) await ctx.send(files=files)
def _cache_len(): if local: return _cache.__len__() return sum(1 for _ in database.scan_iter( match=f"cache.{func.__name__}:*", count=1000))
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