129 lines
4.0 KiB
Python
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
|