Files
croccybot/src/modules/statistics/graphics/stats.py

129 lines
4.0 KiB
Python

from typing import Optional
from datetime import datetime, timedelta
import discord
from meta import LionBot
from gui.cards import StatsCard
from gui.base import CardMode
from tracking.text.data import TextTrackerData
from .. import babel
from ..data import StatsData
_p = babel._p
def format_time(seconds):
return "{:02}:{:02}".format(
int(seconds // 3600),
int(seconds % 3600 // 60)
)
def format_xp(messages, xp):
return f"{messages} ({xp} XP)"
async def get_stats_card(bot: LionBot, userid: int, guildid: int, mode: CardMode):
t = bot.translator.t
data: StatsData = bot.get_cog('StatsCog').data
# TODO: Workouts
# TODO: Leaderboard rankings for this season or all time
guildid = guildid or 0
lion = await bot.core.lions.fetch_member(guildid, userid)
# Calculate the period timestamps, i.e. start time for each summary period
# TODO: Don't do the alltime one like this, not efficient anymore
# TODO: Unless we rewrite study_time_since again?
today = lion.today
month_start = today.replace(day=1)
period_timestamps = (
datetime(1970, 1, 1),
month_start,
today - timedelta(days=today.weekday()),
today
)
# Extract the study times for each period
if mode in (CardMode.STUDY, CardMode.VOICE, CardMode.ANKI):
model = data.VoiceSessionStats
refkey = (guildid or None, userid)
ref_since = model.study_times_since
ref_between = model.study_times_between
period_activity = await ref_since(*refkey, *period_timestamps)
period_strings = [format_time(activity) for activity in reversed(period_activity)]
month_activity = period_activity[1]
month_string = t(_p(
'gui:stats|mode:voice|month',
"{hours} hours"
)).format(hours=int(month_activity // 3600))
elif mode is CardMode.TEXT:
msgmodel = TextTrackerData.TextSessions
if guildid:
model = data.MemberExp
msg_since = msgmodel.member_messages_since
refkey = (guildid, userid)
else:
model = data.UserExp
msg_since = msgmodel.member_messages_between
refkey = (userid,)
ref_since = model.xp_since
ref_between = model.xp_between
xp_period_activity = await ref_since(*refkey, *period_timestamps)
msg_period_activity = await msg_since(*refkey, *period_timestamps)
period_strings = [
format_xp(msgs, xp)
for msgs, xp in zip(reversed(msg_period_activity), reversed(xp_period_activity))
]
month_string = f"{xp_period_activity[1]} XP"
else:
raise ValueError(f"Mode {mode} not supported")
# Get leaderboard position
# TODO: Efficiency
if guildid:
lguild = await bot.core.lions.fetch_guild(guildid)
season_start = lguild.data.season_start
if season_start is not None:
data = await model.leaderboard_since(guildid, season_start)
else:
data = await model.leaderboard_all(guildid)
position = next((i + 1 for i, (uid, _) in enumerate(data) if uid == userid), None)
else:
position = None
# Calculate streak data by requesting times per day
# First calculate starting timestamps for each day
days = list(range(0, today.day + 2))
day_timestamps = [month_start + timedelta(days=day - 1) for day in days]
study_times_month = await ref_between(*refkey, *day_timestamps)
# Then extract streak tuples
streaks = []
streak_start = None
for day, stime in zip(days, study_times_month):
stime = stime or 0
if stime > 0 and streak_start is None:
streak_start = day
elif stime == 0 and streak_start is not None:
streaks.append((streak_start, day-1))
streak_start = None
if streak_start is not None:
streaks.append((streak_start, today.day))
card = StatsCard(
(position, 0),
period_strings,
month_string,
100,
streaks,
skin={'mode': mode}
)
return card