moved biggest_single and worst_offenses into RoboPage

This commit is contained in:
2021-08-15 14:07:44 -05:00
parent d0bf438fea
commit a306f22f13
2 changed files with 52 additions and 36 deletions

42
data.py
View File

@@ -93,6 +93,7 @@ class MsgData:
if len(msg.reactions) > 0: if len(msg.reactions) > 0:
new = reaction_df(msg) new = reaction_df(msg)
async with self.lock: async with self.lock:
# self.reactions = self.reactions.join(new, how='outer')
self.reactions = pd.concat([self.reactions, new]) self.reactions = pd.concat([self.reactions, new])
LOGGER.info(str(new.droplevel(level=0, axis=0).loc[:, 'count'])) LOGGER.info(str(new.droplevel(level=0, axis=0).loc[:, 'count']))
@@ -114,13 +115,17 @@ class MsgData:
# If there were actually some message ids found # If there were actually some message ids found
if message_id_counts.shape[0] > 0: if message_id_counts.shape[0] > 0:
# Select the relevant messages from the self.msgs DataFrame using the filtered message ids
res: pd.DataFrame = self.msgs.loc[message_id_counts] res: pd.DataFrame = self.msgs.loc[message_id_counts]
# Add the 'count' column
res['count'] = counts res['count'] = counts
# If necessary, filter by days into the past
if days is not None and days > 0: if days is not None and days > 0:
res = res[res['created'] >= (datetime.today() - timedelta(days=days)).astimezone()] res = res[res['created'] >= (datetime.today() - timedelta(days=days)).astimezone()]
# return the message DataFrame, sorted by created (sent) time
return res.sort_values('created', ascending=False) return res.sort_values('created', ascending=False)
else: else:
@@ -142,32 +147,19 @@ class MsgData:
def emoji_totals(self, emoji_name: str, days: int = None) -> pd.Series: def emoji_totals(self, emoji_name: str, days: int = None) -> pd.Series:
"""Creates a Series indexed by user id and with the number of reactions with emoji_name as values""" """Creates a Series indexed by user id and with the number of reactions with emoji_name as values"""
return (self messages = self.emoji_messages(emoji_name, days)
.emoji_messages(emoji_name, days) if messages.shape[0] > 0:
.groupby('user id') return (messages
.apply(lambda gdf: gdf['count'].sum()) .groupby('user id')
.sort_values(ascending=False)) .apply(lambda gdf: gdf['count'].sum())
.sort_values(ascending=False))
else:
raise ValueError(f'No messages found for' + \
f' {type(emoji_name)}:{emoji_name}, {type(days)}:{days}')
# return pd.DataFrame()
async def emoji_user_counts(self, client: discord.Client, emoji_name: str, days: int): async def emoji_user_counts(self, client: discord.Client, emoji_name: str, days: int = None):
"""Creates a Series indexed by user display_name with the number of reactions with emoji_name as values"""
counts: pd.Series = self.emoji_totals(emoji_name, days) counts: pd.Series = self.emoji_totals(emoji_name, days)
counts.index = pd.Index([(await client.fetch_user(user_id=uid)).display_name for uid in counts.index]) counts.index = pd.Index([(await client.fetch_user(user_id=uid)).display_name for uid in counts.index])
return counts return counts
def worst_offsenses(self, user: str, days: int):
cdf = self.emoji_messages('cancelled', days=days)
cdf = cdf[cdf['display_name'].str.contains(user, case=False)]
if cdf.shape[0] > 0:
res = f'{user}\'s top 5 cancellations in the last {days} days:\n'
res += f'\n'.join(
f'`{row["count"]:<2.0f}cancellations`\n{row["link"]}' for idx, row in cdf.iloc[:5].iterrows())
else:
res = f'No cancellations for {user} in the past {days} days'
return res
async def biggest_single(self, client: discord.Client, emoji: str, days: int) -> str:
data: pd.Series = self.emoji_totals(emoji_name=emoji, days=days)
user: discord.User = await client.fetch_user(user_id=data.index[0])
LOGGER.info(f'User: {user.mention}')
return f'{user.mention} with {data.iloc[0]:.0f} over the past {int(days)} days'

View File

@@ -4,6 +4,7 @@ import re
from typing import Union from typing import Union
import discord import discord
import pandas as pd
from discord import RawReactionActionEvent, RawReactionClearEmojiEvent from discord import RawReactionActionEvent, RawReactionClearEmojiEvent
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -26,7 +27,7 @@ class RoboPage(discord.Client):
attrs = map(lambda n: getattr(jokes, n)(), attrs) attrs = map(lambda n: getattr(jokes, n)(), attrs)
self.jokes = list(attrs) self.jokes = list(attrs)
self.most_regex = re.compile( self.most_regex = re.compile(
"^who is the most (?P<emoji>\w+)(?: in the past (?P<days>\d+) days)?\??$", "^who is the most\s+(?P<emoji>\S+)\s*?(?:in the past (?P<days>\d+) days)?\??$",
re.IGNORECASE & re.UNICODE, re.IGNORECASE & re.UNICODE,
) )
self.leaderboard_regex = re.compile( self.leaderboard_regex = re.compile(
@@ -43,8 +44,8 @@ class RoboPage(discord.Client):
client=self, client=self,
limit=5000, limit=5000,
days=30, days=30,
# limit=500, # limit=100,
# days=3, # days=14,
) )
self.data.to_sql(self.db_path) self.data.to_sql(self.db_path)
LOGGER.info(f'{self.data.msgs.shape[0]} messages total') LOGGER.info(f'{self.data.msgs.shape[0]} messages total')
@@ -64,15 +65,11 @@ class RoboPage(discord.Client):
return return
elif (m := self.most_regex.match(message.content)) is not None: elif (m := self.most_regex.match(message.content)) is not None:
days = m.group('days') or 14
try: try:
await message.reply( await message.reply(await self.biggest_single(match=m))
await self.data.biggest_single(client=self, except Exception as e:
emoji=get_emoji_name(m.group('emoji')), LOGGER.exception(e)
days=int(days)))
except IndexError as e:
await message.reply('NObody') await message.reply('NObody')
return
else: else:
LOGGER.warning(f'No self.data attribute') LOGGER.warning(f'No self.data attribute')
@@ -100,15 +97,42 @@ class RoboPage(discord.Client):
async def leaderboard(self, match: re.Match) -> str: async def leaderboard(self, match: re.Match) -> str:
emoji_name = get_emoji_name(match.group('emoji')) emoji_name = get_emoji_name(match.group('emoji'))
days = match.group('days') or 14 days = match.group('days') or 14
days = int(days)
counts = await self.data.emoji_user_counts(client=self, counts = await self.data.emoji_user_counts(client=self,
emoji_name=emoji_name, emoji_name=emoji_name,
days=int(days)) days=days)
width = max([len(str(s)) for s in counts.index.values]) width = max([len(str(s)) for s in counts.index.values])
res = f'{match.group("emoji")} totals, past {days} days\n' res = f'{match.group("emoji")} totals, past {days} days\n'
res += '\n'.join(f"`{str(name).ljust(width + 1)}with {cnt:<2.0f} total`" res += '\n'.join(f"`{str(name).ljust(width + 1)}with {cnt:<2.0f} total`"
for name, cnt in counts.iteritems()) for name, cnt in counts.iteritems())
return res return res
async def biggest_single(self, match: re.Match) -> str:
days = match.group('days') or 14
days = int(days)
data: pd.Series = self.data.emoji_totals(
emoji_name=get_emoji_name(match.group('emoji')),
days=days
)
user: discord.User = await client.fetch_user(user_id=data.index[0])
LOGGER.info(f'User: {user.mention}')
msg = f'{user.mention} with {data.iloc[0]:.0f}x {match.group("emoji")} over the past {days} days'
msg += '\n' + await self.worst_offsenses(user=user, days=days, top=3, emoji_str=match.group('emoji'))
return msg
async def worst_offsenses(self, user: discord.User, days: int, top: int, emoji_str: str) -> str:
cdf = self.data.emoji_messages(get_emoji_name(emoji_str), days=days)
cdf = cdf[cdf['user id'] == user.id].sort_values('count', ascending=False).iloc[:top]
if cdf.shape[0] > 0:
res = f'Top {top} {emoji_str}\n'
res += f'\n'.join(
f'{emoji_str}x{row["count"]:.0f}\n{row["link"]}' for idx, row in cdf.iterrows())
else:
res = f'No {emoji_str} for {user} in the past {days} days'
return res
def get_emoji_name(string: str) -> str: def get_emoji_name(string: str) -> str:
if (m := re.search('<:(?P<name>\w+):(?P<id>\d+)>', string)): if (m := re.search('<:(?P<name>\w+):(?P<id>\d+)>', string)):