commit bc248b6486c484c1ae0d227beefccdb99e35bdd4
parent ede67eb6d5db3af8128ffc68f71da75cdef4b019
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date: Sat, 27 Apr 2024 10:41:57 -0700
Update analytics commands to use postgresql db
Diffstat:
6 files changed, 190 insertions(+), 179 deletions(-)
diff --git a/countdown_bot/analyticsCog.py b/countdown_bot/analyticsCog.py
@@ -1,5 +1,5 @@
# Import dependencies
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, timezone
import discord
from discord.ext import commands
from matplotlib import pyplot as plt
@@ -10,15 +10,16 @@ import re
import tempfile
# Import modules
-from .botUtilities import COLORS, getContextCountdown, getUsername, getContributor, CommandError
+from .botUtilities import COLORS, getContextCountdown, getUsername, getContributor, CommandError, CountdownNotFound, getContextCountdown2
from .models import POINT_RULES
class Analytics(commands.Cog):
- def __init__(self, bot, databaseSessionMaker):
+ def __init__(self, bot, databaseSessionMaker, db_connection):
self.bot = bot
self.databaseSessionMaker = databaseSessionMaker
+ self.db_connection = db_connection
@@ -45,9 +46,11 @@ class Analytics(commands.Cog):
Shows information about countdown contributors
"""
- with self.databaseSessionMaker() as session:
+ with self.db_connection.cursor() as cur:
# Get countdown channel
- countdown = getContextCountdown(session, ctx)
+ countdown = getContextCountdown2(cur, ctx)
+ if not countdown:
+ raise CountdownNotFound()
# Create temp file
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
@@ -65,14 +68,20 @@ class Analytics(commands.Cog):
ax.yaxis.set_major_formatter(PercentFormatter())
# Get stats
- contributors = countdown.historicalContributors()
+ cur.execute("SELECT * FROM contributorData(%s);", (countdown,))
+ contributors = [x["userid"] for x in cur.fetchall()]
+ cur.execute("SELECT * FROM historicalContributorData(%s);", (countdown,))
+ data = cur.fetchall()
+
+ if not data:
+ raise CommandError("The countdown doesn't have enough messages yet")
# Plot data and add legend
- for author in list(contributors.keys())[:min(len(contributors), 15)]:
+ for author in contributors[:15]:
# Top 15 contributors get included in the legend
- ax.plot([x["progress"] for x in contributors[author]], [x["percentage"] * 100 for x in contributors[author]], label=await getUsername(self.bot, author))
- for author in list(contributors.keys())[15:max(len(contributors), 15)]:
- ax.plot([x["progress"] for x in contributors[author]], [x["percentage"] * 100 for x in contributors[author]])
+ ax.plot([x["progress"] for x in data if x["userid"] == author], [x["percentage"] for x in data if x["userid"] == author], label=await getUsername(self.bot, author))
+ for author in contributors[15:]:
+ ax.plot([x["progress"] for x in data if x["userid"] == author], [x["percentage"] for x in data if x["userid"] == author])
ax.legend(bbox_to_anchor=(1,1.025), loc="upper left")
# Save graph
@@ -80,39 +89,42 @@ class Analytics(commands.Cog):
file = discord.File(tmp.name, filename="image.png")
# Add content to embed
- embed.description = f"**Countdown Channel:** <#{countdown.id}>"
+ embed.description = f"**Countdown Channel:** <#{countdown}>"
embed.set_image(url="attachment://image.png")
elif (option == ""):
# Create figure
fig, ax = plt.subplots()
# Get stats
- contributors = countdown.contributors()
+ cur.execute("SELECT * FROM contributorData(%s);", (countdown,))
+ data = cur.fetchall()
+
+ if not data:
+ raise CommandError("The countdown doesn't have enough messages yet")
# Add data to graph
- x = [x["author"] for x in contributors]
- y = [x["contributions"] for x in contributors]
- pieData = ax.pie(y, autopct="%1.1f%%", startangle=90)
+ pieData = ax.pie([x["contributions"] for x in data], autopct="%1.1f%%", startangle=90)
# Add legend
- ax.legend(pieData[0], [await getUsername(self.bot, i) for i in x[:min(len(x), 15)]], bbox_to_anchor=(1,1.025), loc="upper left")
+ ax.legend(pieData[0], [await getUsername(self.bot, x["userid"]) for x in
+ data[:15]], bbox_to_anchor=(1,1.025), loc="upper left")
# Save graph
fig.savefig(tmp.name, bbox_inches="tight", pad_inches=0.2)
file = discord.File(tmp.name, filename="image.png")
# Add content to embed
- embed.description = f"**Countdown Channel:** <#{countdown.id}>"
- ranks = ""
- users = ""
- contributions = ""
- for i in range(0, min(len(x), 20)):
- ranks += f"{i+1:,}\n"
- contributions += f"{y[i]:,} *({round(y[i] / len(countdown.messages) * 100, 1)}%)*\n"
- users += f"<@{x[i]}>\n"
- embed.add_field(name="Rank",value=ranks, inline=True)
- embed.add_field(name="User",value=users, inline=True)
- embed.add_field(name="Contributions",value=contributions, inline=True)
+ embed.description = f"**Countdown Channel:** <#{countdown}>"
+ ranksColumn = ""
+ usersColumn = ""
+ contributionsColumn = ""
+ for i in range(0, min(len(data), 20)):
+ ranksColumn += f"{i+1:,}\n"
+ contributionsColumn += f"{data[i]['contributions']:,} *({data[i]['percentage']:.1f}%)*\n"
+ usersColumn += f"<@{data[i]['userid']}>\n"
+ embed.add_field(name="Rank", value=ranksColumn, inline=True)
+ embed.add_field(name="User", value=usersColumn, inline=True)
+ embed.add_field(name="Contributions", value=contributionsColumn, inline=True)
embed.set_image(url="attachment://image.png")
else:
raise CommandError(f"Unrecognized option: `{option}`")
@@ -132,14 +144,16 @@ class Analytics(commands.Cog):
@commands.command(aliases=["e"])
- async def eta(self, ctx, period="24.0"):
+ async def eta(self, ctx):
"""
Shows information about the estimated completion date
"""
- with self.databaseSessionMaker() as session:
+ with self.db_connection.cursor() as cur:
# Get countdown channel
- countdown = getContextCountdown(session, ctx)
+ countdown = getContextCountdown2(cur, ctx)
+ if not countdown:
+ raise CountdownNotFound()
# Create temp file
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
@@ -148,18 +162,12 @@ class Analytics(commands.Cog):
# Create embed
embed=discord.Embed(title=":calendar: Countdown Estimated Completion Date", color=COLORS["embed"])
- # Parse period
- try:
- period = float(period)
- except ValueError:
- raise CommandError(f"Invalid number: `{period}`")
-
- # Make sure period is valid
- if (period < 0.01):
- raise CommandError("The period cannot be less than 0.01 hours")
-
# Get stats
- eta = countdown.eta(timedelta(hours=period))
+ cur.execute("SELECT * FROM etaData(%s);", (countdown,))
+ data = cur.fetchall()
+
+ if not data:
+ raise CommandError("The countdown doesn't have enough messages yet")
# Create figure
fig, ax = plt.subplots()
@@ -167,10 +175,10 @@ class Analytics(commands.Cog):
fig.autofmt_xdate()
# Add ETA data to graph
- ax.plot(eta[0], eta[1], "C0", label="Estimated Completion Date")
+ ax.plot([x["_timestamp"] for x in data], [x["eta"] for x in data], "C0", label="Estimated Completion Date")
# Add reference line graph
- ax.plot([eta[0][0], eta[0][-1]], [eta[0][0], eta[0][-1]], "--C1", label="Current Date")
+ ax.plot([data[0]["_timestamp"], data[-1]["_timestamp"]], [data[0]["_timestamp"], data[-1]["_timestamp"]], "--C1", label="Current Date")
# Add legend
ax.legend()
@@ -180,15 +188,15 @@ class Analytics(commands.Cog):
file = discord.File(tmp.name, filename="image.png")
# Calculate embed data
- maxEta = max(eta[1])
- maxDate = eta[0][eta[1].index(maxEta)]
- minEta = min(eta[1][1:])
- minDate = eta[0][eta[1].index(minEta)]
- end = eta[1][-1] + timedelta(hours=countdown.timezone)
- endDiff = eta[1][-1] - datetime.utcnow()
+ maxEta = max([x["eta"] for x in data])
+ maxDate = [x["_timestamp"] for x in data if x["eta"] == maxEta][0]
+ minEta = min([x["eta"] for x in data])
+ minDate = [x["_timestamp"] for x in data if x["eta"] == minEta][0]
+ end = data[-1]["eta"] # + timedelta(hours=countdown.timezone)
+ endDiff = data[-1]["eta"] - datetime.now(timezone.utc)
# Add content to embed
- embed.description = f"**Countdown Channel:** <#{countdown.id}>\n\n"
+ embed.description = f"**Countdown Channel:** <#{countdown}>\n\n"
embed.description += f"**Maximum Estimate:** {maxEta.date()} (on {maxDate.date()})\n"
embed.description += f"**Minimum Estimate:** {minEta.date()} (on {minDate.date()})\n"
if endDiff < timedelta(seconds=0):
@@ -217,9 +225,11 @@ class Analytics(commands.Cog):
Shows a heatmap of when countdown messages are sent
"""
- with self.databaseSessionMaker() as session:
+ with self.db_connection.cursor() as cur:
# Get countdown channel
- countdown = getContextCountdown(session, ctx)
+ countdown = getContextCountdown2(cur, ctx)
+ if not countdown:
+ raise CountdownNotFound()
# Create temp file
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
@@ -234,8 +244,19 @@ class Analytics(commands.Cog):
else:
userID = await getContributor(self.bot, countdown, user)
- # Get heatmap matrix
- heatmapMatrix = countdown.heatmap(userID)
+ # Get heatmap data
+ cur.execute("SELECT * FROM heatmapData(%s, %s);",
+ (countdown, userID))
+ data = cur.fetchall()
+
+ if not data:
+ print(countdown, userID, data)
+ raise CommandError("The countdown doesn't have enough messages yet")
+
+ # Create heatmap matrix
+ matrix = [[0 for i in range(24)] for j in range(7)]
+ for row in data:
+ matrix[int(row["dow"])][int(row["hour"])] = row["messages"]
# Define hour and weekday names
hours = ["12 AM", "1 AM", "2 AM", "3 AM", "4 AM", "5 AM", "6 AM", "7 AM", "8 AM", "9 AM", "10 AM", "11 AM", "12 PM", "1 PM", "2 PM", "3 PM", "4 PM", "5 PM", "6 PM", "7 PM", "8 PM", "9 PM", "10 PM", "11 PM"]
@@ -253,7 +274,7 @@ class Analytics(commands.Cog):
# Add data to graph
cmap = plt.get_cmap("jet").copy()
cmap.set_bad("gray")
- cax = ax.matshow(np.ma.masked_equal(np.array(heatmapMatrix), 0), cmap=cmap, aspect="auto")
+ cax = ax.matshow(np.ma.masked_equal(np.array(matrix), 0), cmap=cmap, aspect="auto")
fig.colorbar(cax)
# Save graph
@@ -261,17 +282,17 @@ class Analytics(commands.Cog):
file = discord.File(tmp.name, filename="image.png")
# Get embed data
- total = np.sum(heatmapMatrix)
+ total = np.sum(matrix)
averageValue = total / (24*7)
- maxValue = np.max(heatmapMatrix)
- maxWeekday = np.where(heatmapMatrix == maxValue)[0][0]
- maxHour = np.where(heatmapMatrix == maxValue)[1][0]
- currentWeekday = ((datetime.utcnow() + timedelta(hours=countdown.timezone)).weekday() + 1) % 7
- currentHour = (datetime.utcnow() + timedelta(hours=countdown.timezone)).hour
- currentValue = heatmapMatrix[currentWeekday][currentHour]
+ maxValue = np.max(matrix)
+ maxWeekday = np.where(matrix == maxValue)[0][0]
+ maxHour = np.where(matrix == maxValue)[1][0]
+ currentWeekday = (datetime.utcnow().weekday() + 1) % 7 # ((datetime.utcnow() + timedelta(hours=countdown.timezone)).weekday() + 1) % 7
+ currentHour = datetime.utcnow().hour # (datetime.utcnow() + timedelta(hours=countdown.timezone)).hour
+ currentValue = matrix[currentWeekday][currentHour]
# Add content to embed
- embed.description = f"**Countdown Channel:** <#{countdown.id}>\n\n"
+ embed.description = f"**Countdown Channel:** <#{countdown}>\n\n"
if (userID): embed.description += f"**User:** <@{userID}>\n"
embed.description += f"**Total Contributions:** {total:,}\n"
embed.description += f"**Average Contributions per Zone:** {round(averageValue):,}\n"
@@ -299,29 +320,41 @@ class Analytics(commands.Cog):
Shows the countdown leaderboard
"""
- with self.databaseSessionMaker() as session:
+ with self.db_connection.cursor() as cur:
# Get countdown channel
- countdown = getContextCountdown(session, ctx)
-
- # Get leaderboard
- leaderboard = countdown.leaderboard()
+ countdown = getContextCountdown2(cur, ctx)
+ if not countdown:
+ raise CountdownNotFound()
# Create embed
embed=discord.Embed(title=":trophy: Countdown Leaderboard", color=COLORS["embed"])
- # Make sure the countdown has started
+ # Get user
+ if (user == None):
+ userID = None
+ else:
+ userID = await getContributor(self.bot, countdown, user)
+
+ # Get leaderboard
+ cur.execute("SELECT * FROM leaderboardData(%s, %s);",
+ (countdown, userID))
+ data = cur.fetchall()
+
+ if not data:
+ raise CommandError("The countdown doesn't have enough messages yet")
+
if (user is None):
# Add description
- embed.description = f"**Countdown Channel:** <#{countdown.id}>"
+ embed.description = f"**Countdown Channel:** <#{countdown}>"
# Add leaderboard
ranks = ""
points = ""
users = ""
- for i in range(0, min(len(leaderboard), 20)):
- ranks += f"{i+1:,}\n"
- points += f"{leaderboard[i]['points']:,}\n"
- users += f"<@{leaderboard[i]['author']}>\n"
+ for row in data[:20]:
+ ranks += f"{row['ranking']:,}\n"
+ points += f"{row['total']:,}\n"
+ users += f"<@{row['userid']}>\n"
embed.add_field(name="Rank",value=ranks, inline=True)
embed.add_field(name="Points",value=points, inline=True)
embed.add_field(name="User",value=users, inline=True)
@@ -330,34 +363,28 @@ class Analytics(commands.Cog):
rules = ""
values = ""
for rule in POINT_RULES:
- rules += f"{rule}\n"
- values += f"{POINT_RULES[rule]} points\n"
+ rules += f"{POINT_RULES[rule][0]}\n"
+ values += f"{POINT_RULES[rule][1]} points\n"
embed.add_field(name="Rules", value="Only 1 rule is applied towards each number", inline=False)
embed.add_field(name="Numbers", value=rules, inline=True)
embed.add_field(name="Points", value=values, inline=True)
else:
- # Get user rank
- if (re.match("^\d+$", user) and int(user) > 0 and int(user) <= len(leaderboard)):
- rank = int(user) - 1
- else:
- rank = [x["author"] for x in leaderboard].index(await getContributor(self.bot, countdown, user))
-
# Add description
- embed.description = f"**Countdown Channel:** <#{countdown.id}>\n\n"
- embed.description += f"**User:** <@{leaderboard[rank]['author']}>\n"
- embed.description += f"**Rank:** #{rank + 1:,}\n"
- embed.description += f"**Total Points:** {leaderboard[rank]['points']:,}\n"
- embed.description += f"**Total Contributions:** {leaderboard[rank]['contributions']:,} *({round(leaderboard[rank]['contributions'] / len(countdown.messages) * 100, 1)}%)*\n"
+ embed.description = f"**Countdown Channel:** <#{countdown}>\n\n"
+ embed.description += f"**User:** <@{data[0]['userid']}>\n"
+ embed.description += f"**Rank:** #{data[0]['ranking']:,}\n"
+ embed.description += f"**Total Points:** {data[0]['total']:,}\n"
+ embed.description += f"**Total Contributions:** {data[0]['contributions']:,} *({round(data[0]['percentage'])}%)*\n"
# Add points breakdown
rules = ""
points = ""
percentage = ""
- for category in leaderboard[rank]["breakdown"]:
- rules += f"{category}\n"
- points += f"{leaderboard[rank]['breakdown'][category] * POINT_RULES[category]:,} *({leaderboard[rank]['breakdown'][category]:,})*\n"
- if (leaderboard[rank]['points'] > 0):
- percentage += f"{round(leaderboard[rank]['breakdown'][category] * POINT_RULES[category] / leaderboard[rank]['points'] * 100, 1)}%\n"
+ for rule in POINT_RULES:
+ rules += f"{POINT_RULES[rule][0]}\n"
+ points += f"{data[0][rule] * POINT_RULES[rule][1]:,} *({data[0][rule]:,})*\n"
+ if (data[0]['total'] > 0):
+ percentage += f"{round(data[0][rule] * POINT_RULES[rule][1] / data[0]['total'] * 100, 1)}%\n"
else:
percentage += "0%\n"
embed.add_field(name="Category", value=rules, inline=True)
@@ -375,9 +402,11 @@ class Analytics(commands.Cog):
Shows information about countdown progress
"""
- with self.databaseSessionMaker() as session:
+ with self.db_connection.cursor() as cur:
# Get countdown channel
- countdown = getContextCountdown(session, ctx)
+ countdown = getContextCountdown2(cur, ctx)
+ if not countdown:
+ raise CountdownNotFound()
# Create temp file
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
@@ -387,8 +416,13 @@ class Analytics(commands.Cog):
embed=discord.Embed(title=":chart_with_downwards_trend: Countdown Progress", color=COLORS["embed"])
# Get progress stats
- stats = countdown.progress()
- breakStats = countdown.longestBreak()
+ cur.execute("SELECT * FROM progressData(%s);", (countdown,))
+ data = cur.fetchall()
+ cur.execute("CALL progressStats(%s,null,null,null,null,null,null,null,null,null,null);", (countdown,))
+ stats = cur.fetchone()
+
+ if not data:
+ raise CommandError("The countdown doesn't have enough messages yet")
# Create figure
fig, ax = plt.subplots()
@@ -397,8 +431,8 @@ class Analytics(commands.Cog):
fig.autofmt_xdate()
# Add data to graph
- x = [stats["start"] + timedelta(hours=countdown.timezone)] + [x["time"] + timedelta(hours=countdown.timezone) for x in stats["progress"]]
- y = [0] + [x["progress"] for x in stats["progress"]]
+ x = [data[0]["_timestamp"]] + [x["_timestamp"] for x in data]
+ y = [0] + [x["progress"] for x in data]
ax.plot(x, y)
# Save graph
@@ -406,17 +440,17 @@ class Analytics(commands.Cog):
file = discord.File(tmp.name, filename="image.png")
# Calculate embed data
- longestBreakDuration = timedelta(days=breakStats['duration'].days, seconds=breakStats['duration'].seconds)
- longestBreakStart = breakStats['start'].date()
- longestBreakEnd = breakStats['end'].date()
- start = (stats["start"] + timedelta(hours=countdown.timezone)).date()
- startDiff = (datetime.utcnow() - stats["start"]).days
- end = (stats["eta"] + timedelta(hours=countdown.timezone)).date()
- endDiff = stats["eta"] - datetime.utcnow()
+ longestBreakDuration = timedelta(days=stats["longestbreak"].days, seconds=stats["longestbreak"].seconds)
+ longestBreakStart = stats["longestbreakstart"].date()
+ longestBreakEnd = stats["longestbreakend"].date()
+ start = stats["starttime"].date() # (stats["starttime"] + timedelta(hours=countdown.timezone)).date()
+ startDiff = (datetime.now(timezone.utc) - stats["starttime"]).days
+ end = stats["endtime"].date() # (stats["endtime"] + timedelta(hours=countdown.timezone)).date()
+ endDiff = stats["endtime"] - datetime.now(timezone.utc)
# Add content to embed
- embed.description = f"**Countdown Channel:** <#{countdown.id}>\n\n"
- embed.description += f"**Progress:** {stats['total'] - stats['current']:,} / {stats['total']:,} ({round(stats['percentage'], 1)}%)\n"
+ embed.description = f"**Countdown Channel:** <#{countdown}>\n\n"
+ embed.description += f"**Progress:** {stats['progress']:,} / {stats['total']:,} ({round(stats['percentage'], 1)}%)\n"
embed.description += f"**Average Progress per Day:** {round(stats['rate']):,}\n"
embed.description += f"**Longest Break:** {longestBreakDuration} ({longestBreakStart} to {longestBreakEnd})\n"
embed.description += f"**Start Date:** {start} ({startDiff:,} days ago)\n"
@@ -441,14 +475,16 @@ class Analytics(commands.Cog):
@commands.command(aliases=["s"])
- async def speed(self, ctx, period="24.0"):
+ async def speed(self, ctx, period="24"):
"""
Shows information about countdown speed
"""
- with self.databaseSessionMaker() as session:
+ with self.db_connection.cursor() as cur:
# Get countdown channel
- countdown = getContextCountdown(session, ctx)
+ countdown = getContextCountdown2(cur, ctx)
+ if not countdown:
+ raise CountdownNotFound()
# Create temp file
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
@@ -459,18 +495,16 @@ class Analytics(commands.Cog):
# Parse period
try:
- period = float(period)
+ period = int(period)
except ValueError:
raise CommandError(f"Invalid number: `{period}`")
- # Make sure period is valid
- if (period < 0.01):
- raise CommandError("The period cannot be less than 0.01 hours")
+ # Get data
+ cur.execute("SELECT * FROM speedData(%s, %s);", (countdown, period))
+ data = cur.fetchall()
- # Get stats
- stats = countdown.progress()
- period = timedelta(hours=period)
- speed = countdown.speed(period)
+ if not data:
+ raise CommandError("The countdown doesn't have enough messages yet")
# Create figure
fig, ax = plt.subplots()
@@ -479,24 +513,27 @@ class Analytics(commands.Cog):
fig.autofmt_xdate()
# Add data to graph
- for i in range(0, len(speed[0])):
- ax.bar(speed[0][i], speed[1][i], width=period, align="edge", color="#1f77b4")
+ period = timedelta(hours=period)
+ for row in data:
+ ax.bar(row["periodstart"], row["messages"], width=period, align="edge", color="#1f77b4")
# Save graph
fig.savefig(tmp.name, bbox_inches="tight", pad_inches=0.2)
file = discord.File(tmp.name, filename="image.png")
+ # Calculate embed data
+ maxSpeed = max([x["messages"] for x in data])
+ avgSpeed = round(sum([x["messages"] for x in data]) / len(data))
+ curSpeed = data[-1]["messages"]
+ curPeriod = data[-1]["periodstart"]
+
# Add content to embed
- embed.description = f"**Countdown Channel:** <#{countdown.id}>\n\n"
+ embed.description = f"**Countdown Channel:** <#{countdown}>\n\n"
embed.description += f"**Period Size:** {period}\n"
- if (len(countdown.messages) > 1):
- rate = (stats['total'] - stats['current'])/((countdown.messages[-1].timestamp - countdown.messages[0].timestamp) / period)
- else:
- rate = 0
- embed.description += f"**Average Progress per Period:** {round(rate):,}\n"
- embed.description += f"**Record Progress per Period:** {max(speed[1]):,}\n"
- embed.description += f"**Last Period Start:** {speed[0][-1]}\n"
- embed.description += f"**Progress during Last Period:** {speed[1][-1]:,}\n"
+ embed.description += f"**Average Progress per Period:** {avgSpeed:,}\n"
+ embed.description += f"**Record Progress per Period:** {maxSpeed:,}\n"
+ embed.description += f"**Last Period Start:** {curPeriod}\n"
+ embed.description += f"**Progress during Last Period:** {curSpeed:,}\n"
embed.set_image(url="attachment://image.png")
# Send embed
diff --git a/countdown_bot/bot.py b/countdown_bot/bot.py
@@ -30,7 +30,7 @@ class CountdownBot(commands.Bot):
async def setup_hook(self):
await self.add_cog(utilitiesCog.Utilities(self, self.databaseSessionMaker, self.db_connection))
- await self.add_cog(analyticsCog.Analytics(self, self.databaseSessionMaker))
+ await self.add_cog(analyticsCog.Analytics(self, self.databaseSessionMaker, self.db_connection))
diff --git a/countdown_bot/botUtilities.py b/countdown_bot/botUtilities.py
@@ -96,32 +96,8 @@ async def getContributor(bot, countdown, text):
If a matching contributor cannot be found
"""
- # Get countdown contributors
- contributors = [x["author"] for x in countdown.contributors()]
-
- # Get user from mention
- if (re.match("^<@!\d+>$", text) and int(text[3:-1]) in contributors):
- return int(text[3:-1])
- elif (re.match("^<@!\d+>$", text)):
- raise ContributorNotFound(text)
-
- # Get user from username
- for contributor in contributors:
- try:
- username = await getUsername(bot, contributor)
- except:
- continue
- if (username.lower().startswith(text.lower())):
- return contributor
-
- # Get user from nickname
- for contributor in contributors:
- try:
- nickname = await getNickname(bot, countdown.server_id, contributor)
- except:
- continue
- if (nickname.lower().startswith(text.lower())):
- return contributor
+ if (re.match("^<@\d+>$", text)):
+ return int(text[2:-1])
raise ContributorNotFound(text)
@@ -258,7 +234,7 @@ def getPrefix(conn, ctx, default):
with conn.cursor() as cur:
cur.execute("SELECT * FROM getPrefixes(%s, %s);",
- (ctx.channel.guild.id, ctx.channel.id))
+ (ctx.channel.guild.id if ctx.channel.guild else None, ctx.channel.id))
prefixes = cur.fetchall()
return [x["prefix"] for x in prefixes] if prefixes else default
diff --git a/countdown_bot/models.py b/countdown_bot/models.py
@@ -29,16 +29,16 @@ def getSessionMaker(location):
# The rules for awarding leaderboard points
POINT_RULES = {
- "1000s": 1000,
- "1001s": 500,
- "200s": 200,
- "201s": 100,
- "100s": 100,
- "101s": 50,
- "Prime Numbers": 15,
- "Odd Numbers": 12,
- "Even Numbers": 10,
- "First Number": 0,
+ "r1": ("First Number", 0),
+ "r2": ("1000s", 1000),
+ "r3": ("1001s", 500),
+ "r4": ("200s", 200),
+ "r5": ("201s", 100),
+ "r6": ("100s", 100),
+ "r7": ("101s", 50),
+ # "r8": ("Prime Numbers", 15),
+ "r8": ("Odd Numbers", 12),
+ "r9": ("Even Numbers", 10),
}
diff --git a/countdown_bot/utilitiesCog.py b/countdown_bot/utilitiesCog.py
@@ -260,13 +260,11 @@ class Utilities(commands.Cog):
"eta":
"**Name:** eta\n" \
"**Description:** Shows information about the estimated completion date\n" \
- f"**Usage:** `{prefixes[0]}eta|e [<period>]`\n" \
+ f"**Usage:** `{prefixes[0]}eta|e`\n" \
"**Aliases:** `e`\n" \
- "**Arguments:**\n" \
- "**-** `<period>`: The size of the period in hours (the default is 24 hours)\n" \
+ "**Arguments:** none\n" \
"**Examples:**\n" \
f"**-** `{prefixes[0]}eta`\n" \
- f"**-** `{prefixes[0]}eta 48`\n" \
"**Notes:** none\n",
"heatmap":
"**Name:** heatmap\n" \
@@ -274,11 +272,10 @@ class Utilities(commands.Cog):
f"**Usage:** `{prefixes[0]}heatmap [<user>]`\n" \
"**Aliases:** none\n" \
"**Arguments:**\n" \
- "**-** `<user>`: The username or nickname of the user to view heatmap information about. If no value is supplied, the general heatmap will be shown\n" \
+ "**-** `<user>`: The user to view heatmap information about. If no value is supplied, the general heatmap will be shown\n" \
"**Examples:**\n" \
f"**-** `{prefixes[0]}heatmap`\n" \
f"**-** `{prefixes[0]}heatmap @Alice`\n" \
- f"**-** `{prefixes[0]}heatmap Bob`\n" \
"**Notes:** none\n",
"help":
"**Name:** help\n" \
@@ -297,12 +294,10 @@ class Utilities(commands.Cog):
f"**Usage:** `{prefixes[0]}leaderboard|l [<user>]`\n" \
"**Aliases:** `l`\n" \
"**Arguments:**\n" \
- "**-** `<user>`: The rank, username, or nickname of the user to view leaderboard information about. If no value is supplied, the whole leaderboard will be shown\n" \
+ "**-** `<user>`: The user to view leaderboard information about. If no value is supplied, the whole leaderboard will be shown\n" \
"**Examples:**\n" \
f"**-** `{prefixes[0]}leaderboard`\n" \
- f"**-** `{prefixes[0]}leaderboard 1`\n" \
f"**-** `{prefixes[0]}leaderboard @Alice`\n" \
- f"**-** `{prefixes[0]}leaderboard Bob`\n" \
"**Notes:** The leaderboard embed will only show the top 20 contributors\n",
"ping":
"**Name:** ping\n" \
diff --git a/models/analytics.sql b/models/analytics.sql
@@ -66,12 +66,12 @@ BEGIN
RETURN QUERY
SELECT
timestamp,
- to_timestamp(startTime + value *
+ to_timestamp(startTime + total *
(extract(epoch FROM timestamp) - startTime) / (total - value)
) AS eta
FROM messages
WHERE countdownID = _countdownID
- AND extract(epoch FROM timestamp) != startTime
+ AND value != total
ORDER BY messageID;
END
$$;
@@ -298,7 +298,10 @@ BEGIN
-- Calculate longestBreak, longestBreakStart, and longestBreakEnd
SELECT
timestamp,
- LEAD(timestamp, 1, NOW()) OVER (ORDER BY timestamp) - timestamp AS delta
+ CASE
+ WHEN value = 0 THEN '0'
+ ELSE LEAD(timestamp, 1, NOW()) OVER (ORDER BY timestamp) - timestamp
+ END AS delta
INTO longestBreakStart, longestBreak
FROM messages
WHERE messages.countdownID = _countdownID