From 0e2113eefd2e3c34db2e1565e1a76caccfb571a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?= Date: Fri, 13 Oct 2023 16:32:12 +0200 Subject: [PATCH] Display durations in a consistent manner Fixes #61 --- CHANGELOG.md | 8 ++++++++ common/time.py | 31 ++++++++++++++++++++++--------- games/models.py | 2 +- games/views.py | 4 ++-- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6411024..fc2b554 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.1.2 / 2023-10-13 16:30+02:00 + +### Enhancements +* Durations are formatted in a consisent manner across all pages + +### Fixes +* Game Overview: display duration when >1 hour instead of displaying 0 + ## 1.1.1 / 2023-10-09 20:52+02:00 ### New diff --git a/common/time.py b/common/time.py index 1106546..918ca5c 100644 --- a/common/time.py +++ b/common/time.py @@ -32,6 +32,8 @@ def format_duration( from the formatting string. For example: - 61 seconds as "%s" = 61 seconds - 61 seconds as "%m %s" = 1 minutes 1 seconds" + Format specifiers can include width and precision options: + - %5.2H: hours formatted with width 5 and 2 decimal places (padded with zeros) """ minute_seconds = 60 hour_seconds = 60 * minute_seconds @@ -46,18 +48,29 @@ def format_duration( remainder = seconds = seconds_total if "%d" in format_string: days, remainder = divmod(seconds_total, day_seconds) - if "%H" in format_string: - hours, remainder = divmod(remainder, hour_seconds) - if "%m" in format_string: + if re.search(r"%\d*\.?\d*H", format_string): + hours_float, remainder = divmod(remainder, hour_seconds) + hours = float(hours_float) + remainder / hour_seconds + if re.search(r"%\d*\.?\d*m", format_string): minutes, seconds = divmod(remainder, minute_seconds) literals = { - "%d": str(days), - "%H": str(hours), - "%m": str(minutes), - "%s": str(seconds), - "%r": str(seconds_total), + "d": str(days), + "H": str(hours), + "m": str(minutes), + "s": str(seconds), + "r": str(seconds_total), } formatted_string = format_string for pattern, replacement in literals.items(): - formatted_string = re.sub(pattern, replacement, formatted_string) + # Match format specifiers with optional width and precision + match = re.search(rf"%(\d*\.?\d*){pattern}", formatted_string) + if match: + format_spec = match.group(1) + if format_spec: + # Format the number according to the specifier + replacement = f"{float(replacement):{format_spec}f}" + # Replace the format specifier with the formatted number + formatted_string = re.sub( + rf"%\d*\.?\d*{pattern}", replacement, formatted_string + ) return formatted_string diff --git a/games/models.py b/games/models.py index c1d0265..22865e0 100644 --- a/games/models.py +++ b/games/models.py @@ -114,7 +114,7 @@ class Session(models.Model): return timedelta(seconds=(manual + calculated).total_seconds()) def duration_formatted(self) -> str: - result = format_duration(self.duration_seconds(), "%H:%m") + result = format_duration(self.duration_seconds(), "%02.0H:%02.0m") return result @property diff --git a/games/views.py b/games/views.py index 1684f79..c622f18 100644 --- a/games/views.py +++ b/games/views.py @@ -101,8 +101,8 @@ def view_game(request, game_id=None): context["sessions"] = Session.objects.filter( purchase__edition__game_id=game_id ).order_by("-timestamp_start") - context["total_hours"] = int( - format_duration(context["sessions"].total_duration_unformatted(), "%H") + context["total_hours"] = float( + format_duration(context["sessions"].total_duration_unformatted(), "%2.1H") ) context["session_average"] = round( (context["total_hours"]) / int(context["sessions"].count()), 1