From 76dc6a456600ae0071bce58f5f2eeddb233c01d7 Mon Sep 17 00:00:00 2001 From: Conatum Date: Mon, 11 Sep 2023 09:46:02 +0300 Subject: [PATCH] ui(stats): Improve visibility of season start. --- src/modules/ranks/cog.py | 2 +- src/modules/ranks/ui/overview.py | 37 +++++++++++++++++++++++- src/modules/statistics/cog.py | 8 +---- src/modules/statistics/settings.py | 9 ++++-- src/modules/statistics/ui/leaderboard.py | 21 ++++++++++---- src/settings/setting_types.py | 2 +- 6 files changed, 61 insertions(+), 18 deletions(-) diff --git a/src/modules/ranks/cog.py b/src/modules/ranks/cog.py index 3cc61bad..8e245cf6 100644 --- a/src/modules/ranks/cog.py +++ b/src/modules/ranks/cog.py @@ -693,7 +693,7 @@ class RankCog(LionCog): await ui.wait() else: await ui.reload() - msg = await ui.make_message() + msg = await ui.make_message(show_note=False) await ctx.reply( **msg.send_args, ephemeral=True diff --git a/src/modules/ranks/ui/overview.py b/src/modules/ranks/ui/overview.py index 90741832..829e86ea 100644 --- a/src/modules/ranks/ui/overview.py +++ b/src/modules/ranks/ui/overview.py @@ -349,7 +349,7 @@ class RankOverviewUI(MessageUI): string = f"{start} msgs" return string - async def make_message(self) -> MessageArgs: + async def make_message(self, show_note=True) -> MessageArgs: t = self.bot.translator.t if self.ranks: @@ -380,6 +380,34 @@ class RankOverviewUI(MessageUI): ] or [[]] lines = line_blocks[self.pagen] desc = '\n'.join(reversed(lines)) + + # Add note about season start + note_name = t(_p( + 'ui:rank_overview|embed|field:note|name', + "Note" + )) + season_start = self.lguild.data.season_start + if season_start: + season_str = t(_p( + 'ui:rank_overview|embed|field:note|value:with_season', + "Ranks are determined by activity since {timestamp}." + )).format( + timestamp=discord.utils.format_dt(season_start) + ) + else: + season_str = t(_p( + 'ui:rank_overview|embed|field:note|value:without_season', + "Ranks are determined by *all-time* statistics.\n" + "To reward ranks from a later time (e.g. to have monthly/quarterly/yearly ranks) " + "set the `season_start` with {stats_cmd}" + )).format(stats_cmd=self.bot.core.mention_cmd('configure statistics')) + if self.rank_type is RankType.VOICE: + addendum = t(_p( + 'ui:rank_overview|embed|field:note|value|voice_addendum', + "Also note that ranks will only be updated when a member leaves a tracked voice channel! " + "Use the **Refresh Member Ranks** button below to update all members manually." + )) + season_str = '\n'.join((season_str, addendum)) else: # No ranks, give hints about adding ranks desc = t(_p( @@ -410,6 +438,13 @@ class RankOverviewUI(MessageUI): title=title, description=desc ) + if show_note: + embed.add_field( + name=note_name, + value=season_str, + inline=False + ) + return MessageArgs(embed=embed) async def refresh_layout(self): diff --git a/src/modules/statistics/cog.py b/src/modules/statistics/cog.py index b778f039..50086dff 100644 --- a/src/modules/statistics/cog.py +++ b/src/modules/statistics/cog.py @@ -141,13 +141,7 @@ class StatsCog(LionCog): # Send update ack if modified: - # TODO - description = t(_p( - 'cmd:configure_statistics|resp:success|desc', - "Activity ranks and season leaderboard will now be measured from {season_start}." - )).format( - season_start=setting_season_start.formatted - ) + description = setting_season_start.update_message embed = discord.Embed( colour=discord.Colour.brand_green(), description=description diff --git a/src/modules/statistics/settings.py b/src/modules/statistics/settings.py index 236ee0d5..375c577f 100644 --- a/src/modules/statistics/settings.py +++ b/src/modules/statistics/settings.py @@ -94,7 +94,8 @@ class StatisticsSettings(SettingGroup): 'guildset:season_start|long_desc', "Activity ranks will be determined based on tracked activity since this time, " "and the leaderboard will display activity since this time by default. " - "Unset to disable seasons and use all-time statistics instead." + "Unset to disable seasons and use all-time statistics instead.\n" + "Provided dates and times are assumed to be in the guild `timezone`, so set this first!" ) _accepts = _p( 'guildset:season_start|accepts', @@ -134,7 +135,8 @@ class StatisticsSettings(SettingGroup): resp = t(_p( 'guildset:season_start|set_response|set', "The leaderboard season and activity ranks will now count from {timestamp}. " - "Member ranks will update when they are next active. Use {rank_cmd} to refresh immediately." + "Member ranks will update when they are next active.\n" + "Use {rank_cmd} and press **Refresh Member Ranks** to refresh all ranks immediately." )).format( timestamp=self.formatted, rank_cmd=bot.core.mention_cmd('ranks') @@ -143,7 +145,8 @@ class StatisticsSettings(SettingGroup): resp = t(_p( 'guildset:season_start|set_response|unset', "The leaderboard and activity ranks will now count all-time statistics. " - "Member ranks will update when they are next active. Use {rank_cmd} to refresh immediately." + "Member ranks will update when they are next active.\n" + "Use {rank_cmd} and press **Refresh Member Ranks** to refresh all ranks immediately." )).format(rank_cmd=bot.core.mention_cmd('ranks')) return resp diff --git a/src/modules/statistics/ui/leaderboard.py b/src/modules/statistics/ui/leaderboard.py index 4abbbfcc..cc82c2ff 100644 --- a/src/modules/statistics/ui/leaderboard.py +++ b/src/modules/statistics/ui/leaderboard.py @@ -90,6 +90,8 @@ class LeaderboardUI(StatsUI): periods[LBPeriod.DAY] = lguild.today periods[LBPeriod.WEEK] = lguild.week_start periods[LBPeriod.MONTH] = lguild.month_start + alltime = (lguild.data.first_joined_at or interaction.guild.created_at).astimezone(lguild.timezone) + periods[LBPeriod.ALLTIME] = alltime self.period_starts = periods self.focused = True @@ -432,28 +434,37 @@ class LeaderboardUI(StatsUI): """ Generate UI message arguments from stored data """ + t = self.bot.translator.t if self.card is not None: + period_start = self.period_starts[self.current_period] + header = t(_p( + 'ui:leaderboard|since', + "Counting statistics since {timestamp}" + )).format(timestamp=discord.utils.format_dt(period_start)) args = MessageArgs( embed=None, + content=header, file=self.card.as_file('leaderboard.png') ) else: - t = self.bot.translator.t if self.stat_type is StatType.VOICE: empty_description = t(_p( 'ui:leaderboard|mode:voice|message:empty|desc', - "There has been no voice activity in this period!" + "There has been no voice activity since {timestamp}" )) elif self.stat_type is StatType.TEXT: empty_description = t(_p( 'ui:leaderboard|mode:text|message:empty|desc', - "There has been no message activity in this period!" + "There has been no message activity since {timestamp}" )) elif self.stat_type is StatType.ANKI: empty_description = t(_p( 'ui:leaderboard|mode:anki|message:empty|desc', - "There have been no Anki cards reviewed in this period!" + "There have been no Anki cards reviewed since {timestamp}" )) + empty_description = empty_description.format( + timestamp=discord.utils.format_dt(self.period_starts[self.current_period]) + ) embed = discord.Embed( colour=discord.Colour.orange(), title=t(_p( @@ -462,7 +473,7 @@ class LeaderboardUI(StatsUI): )), description=empty_description ) - args = MessageArgs(embed=embed, files=[]) + args = MessageArgs(content=None, embed=embed, files=[]) return args async def refresh_components(self): diff --git a/src/settings/setting_types.py b/src/settings/setting_types.py index 09f6e2a0..2dd67815 100644 --- a/src/settings/setting_types.py +++ b/src/settings/setting_types.py @@ -1004,7 +1004,7 @@ class TimestampSetting(InteractiveSetting[ParentID, str, dt.datetime]): @property def input_formatted(self) -> str: if self._data: - formatted = self._data.strftime('%Y-%M-%d %H:%M') + formatted = self._data.strftime('%Y-%m-%d %H:%M') else: formatted = '' return formatted