Display durations in a consistent manner

Fixes #61
This commit is contained in:
Lukáš Kucharczyk 2023-10-13 16:32:12 +02:00
parent c4b0347f3b
commit 0e2113eefd
4 changed files with 33 additions and 12 deletions

View File

@ -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 ## 1.1.1 / 2023-10-09 20:52+02:00
### New ### New

View File

@ -32,6 +32,8 @@ def format_duration(
from the formatting string. For example: from the formatting string. For example:
- 61 seconds as "%s" = 61 seconds - 61 seconds as "%s" = 61 seconds
- 61 seconds as "%m %s" = 1 minutes 1 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 minute_seconds = 60
hour_seconds = 60 * minute_seconds hour_seconds = 60 * minute_seconds
@ -46,18 +48,29 @@ def format_duration(
remainder = seconds = seconds_total remainder = seconds = seconds_total
if "%d" in format_string: if "%d" in format_string:
days, remainder = divmod(seconds_total, day_seconds) days, remainder = divmod(seconds_total, day_seconds)
if "%H" in format_string: if re.search(r"%\d*\.?\d*H", format_string):
hours, remainder = divmod(remainder, hour_seconds) hours_float, remainder = divmod(remainder, hour_seconds)
if "%m" in format_string: hours = float(hours_float) + remainder / hour_seconds
if re.search(r"%\d*\.?\d*m", format_string):
minutes, seconds = divmod(remainder, minute_seconds) minutes, seconds = divmod(remainder, minute_seconds)
literals = { literals = {
"%d": str(days), "d": str(days),
"%H": str(hours), "H": str(hours),
"%m": str(minutes), "m": str(minutes),
"%s": str(seconds), "s": str(seconds),
"%r": str(seconds_total), "r": str(seconds_total),
} }
formatted_string = format_string formatted_string = format_string
for pattern, replacement in literals.items(): 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 return formatted_string

View File

@ -114,7 +114,7 @@ class Session(models.Model):
return timedelta(seconds=(manual + calculated).total_seconds()) return timedelta(seconds=(manual + calculated).total_seconds())
def duration_formatted(self) -> str: 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 return result
@property @property

View File

@ -101,8 +101,8 @@ def view_game(request, game_id=None):
context["sessions"] = Session.objects.filter( context["sessions"] = Session.objects.filter(
purchase__edition__game_id=game_id purchase__edition__game_id=game_id
).order_by("-timestamp_start") ).order_by("-timestamp_start")
context["total_hours"] = int( context["total_hours"] = float(
format_duration(context["sessions"].total_duration_unformatted(), "%H") format_duration(context["sessions"].total_duration_unformatted(), "%2.1H")
) )
context["session_average"] = round( context["session_average"] = round(
(context["total_hours"]) / int(context["sessions"].count()), 1 (context["total_hours"]) / int(context["sessions"].count()), 1