diff options
author | 2018-08-23 19:25:10 +0000 | |
---|---|---|
committer | 2018-08-23 19:25:10 +0000 | |
commit | 5e5cd475b6baf7b1fed0108b28dc249ecd425c28 (patch) | |
tree | f7f81cb7dd102840d4a868195dbff291317ab975 | |
parent | Merge branch 'eval-indent-fix' into 'master' (diff) |
Better timedelta humanization
-rw-r--r-- | bot/cogs/antispam.py | 2 | ||||
-rw-r--r-- | bot/cogs/information.py | 22 | ||||
-rw-r--r-- | bot/cogs/modlog.py | 6 | ||||
-rw-r--r-- | bot/utils/time.py | 90 |
4 files changed, 71 insertions, 49 deletions
diff --git a/bot/cogs/antispam.py b/bot/cogs/antispam.py index 65c3f0b4b..7a33ba9e8 100644 --- a/bot/cogs/antispam.py +++ b/bot/cogs/antispam.py @@ -15,7 +15,7 @@ from bot.constants import ( Colours, DEBUG_MODE, Event, Guild as GuildConfig, Icons, Roles, ) -from bot.utils.time import humanize as humanize_delta +from bot.utils.time import humanize_delta log = logging.getLogger(__name__) diff --git a/bot/cogs/information.py b/bot/cogs/information.py index 2390334d8..a313d2379 100644 --- a/bot/cogs/information.py +++ b/bot/cogs/information.py @@ -1,14 +1,12 @@ import logging import textwrap -from datetime import datetime -from dateutil.relativedelta import relativedelta from discord import CategoryChannel, Colour, Embed, Member, TextChannel, VoiceChannel from discord.ext.commands import Bot, Context, command from bot.constants import Emojis, Keys, Roles, URLs from bot.decorators import with_role -from bot.utils.time import humanize +from bot.utils.time import time_since log = logging.getLogger(__name__) @@ -59,9 +57,7 @@ class Information: server information. """ - now = datetime.now() - created_delta = relativedelta(now, ctx.guild.created_at) - created = humanize(created_delta, accuracy="days") + created = time_since(ctx.guild.created_at, precision="days") features = ", ".join(ctx.guild.features) region = ctx.guild.region @@ -100,7 +96,7 @@ class Information: colour=Colour.blurple(), description=textwrap.dedent(f""" **Server information** - Created: {created} ago + Created: {created} Voice region: {region} Features: {features} @@ -132,19 +128,15 @@ class Information: if user is None: user = ctx.author - now = datetime.now() - # User information - created_delta = relativedelta(now, user.created_at) - created = humanize(created_delta, accuracy="days") + created = time_since(user.created_at, max_units=3) name = f"{user.name}#{user.discriminator}" if user.nick: name = f"{user.nick} ({name})" # Member information - joined_delta = relativedelta(now, user.joined_at) - joined = humanize(joined_delta, accuracy="days") + joined = time_since(user.joined_at, precision="days") # You're welcome, Volcyyyyyyyyyyyyyyyy roles = ", ".join( @@ -174,12 +166,12 @@ class Information: title=name, description=textwrap.dedent(f""" **User Information** - Created: {created} ago + Created: {created} Profile: {user.mention} ID: {user.id} **Member Information** - Joined: {joined} ago + Joined: {joined} Roles: {roles or None} **Infractions** diff --git a/bot/cogs/modlog.py b/bot/cogs/modlog.py index 226c62952..2f72d92fc 100644 --- a/bot/cogs/modlog.py +++ b/bot/cogs/modlog.py @@ -16,8 +16,7 @@ from discord.ext.commands import Bot from bot.constants import Channels, Colours, Emojis, Event, Icons, Keys, Roles, URLs from bot.constants import Guild as GuildConstant -from bot.utils.time import humanize - +from bot.utils.time import humanize_delta log = logging.getLogger(__name__) @@ -358,11 +357,10 @@ class ModLog: return message = f"{member.name}#{member.discriminator} (`{member.id}`)" - now = datetime.datetime.utcnow() difference = abs(relativedelta(now, member.created_at)) - message += "\n\n**Account age:** " + humanize(difference) + message += "\n\n**Account age:** " + humanize_delta(member.created_at) if difference.days < 1 and difference.months < 1 and difference.years < 1: # New user account! message = f"{Emojis.new} {message}" diff --git a/bot/utils/time.py b/bot/utils/time.py index b3f55932c..77cef4670 100644 --- a/bot/utils/time.py +++ b/bot/utils/time.py @@ -1,59 +1,91 @@ +import datetime + from dateutil.relativedelta import relativedelta -def _plural_timestring(value: int, unit: str) -> str: +def _stringify_time_unit(value: int, unit: str): """ - Takes a value and a unit type, - such as 24 and "hours". - - Returns a string that takes - the correct plural into account. + Returns a string to represent a value and time unit, + ensuring that it uses the right plural form of the unit. - >>> _plural_timestring(1, "seconds") + >>> _stringify_time_unit(1, "seconds") "1 second" - >>> _plural_timestring(24, "hours") + >>> _stringify_time_unit(24, "hours") "24 hours" + >>> _stringify_time_unit(0, "minutes") + "less than a minute" """ if value == 1: return f"{value} {unit[:-1]}" + elif value == 0: + return f"less than a {unit[:-1]}" else: return f"{value} {unit}" -def humanize(delta: relativedelta, accuracy: str = "seconds") -> str: +def humanize_delta(delta: relativedelta, precision: str = "seconds", max_units: int = 6): """ - This takes a relativedelta and - returns a nice human readable string. + Returns a human-readable version of the relativedelta. - "4 days, 12 hours and 1 second" + :param delta: A dateutil.relativedelta.relativedelta object + :param precision: The smallest unit that should be included. + :param max_units: The maximum number of time-units to return. - :param delta: A dateutils.relativedelta.relativedelta object - :param accuracy: The smallest unit that should be included. - :return: A humanized string. + :return: A string like `4 days, 12 hours and 1 second`, + `1 minute`, or `less than a minute`. """ - units = { - "years": delta.years, - "months": delta.months, - "days": delta.days, - "hours": delta.hours, - "minutes": delta.minutes, - "seconds": delta.seconds - } + units = ( + ("years", delta.years), + ("months", delta.months), + ("days", delta.days), + ("hours", delta.hours), + ("minutes", delta.minutes), + ("seconds", delta.seconds), + ) - # Add the time units that are >0, but stop at accuracy. + # Add the time units that are >0, but stop at accuracy or max_units. time_strings = [] - for unit, value in units.items(): + unit_count = 0 + for unit, value in units: if value: - time_strings.append(_plural_timestring(value, unit)) + time_strings.append(_stringify_time_unit(value, unit)) + unit_count += 1 - if unit == accuracy: + if unit == precision or unit_count >= max_units: break - # Add the 'and' between the last two units + # Add the 'and' between the last two units, if necessary if len(time_strings) > 1: time_strings[-1] = f"{time_strings[-2]} and {time_strings[-1]}" del time_strings[-2] - return ", ".join(time_strings) + # If nothing has been found, just make the value 0 precision, e.g. `0 days`. + if not time_strings: + humanized = _stringify_time_unit(0, precision) + else: + humanized = ", ".join(time_strings) + + return humanized + + +def time_since(past_datetime: datetime.datetime, precision: str = "seconds", max_units: int = 6): + """ + Takes a datetime and returns a human-readable string that + describes how long ago that datetime was. + + :param past_datetime: A datetime.datetime object + :param precision: The smallest unit that should be included. + :param max_units: The maximum number of time-units to return. + + :return: A string like `4 days, 12 hours and 1 second ago`, + `1 minute ago`, or `less than a minute ago`. + """ + + now = datetime.datetime.utcnow() + delta = abs(relativedelta(now, past_datetime)) + + humanized = humanize_delta(delta, precision, max_units) + + return f"{humanized} ago" |