Continue making use of improved duration handling
All checks were successful
Django CI/CD / test (push) Successful in 1m5s
Django CI/CD / build-and-push (push) Successful in 2m22s

This commit is contained in:
Lukáš Kucharczyk 2025-03-25 23:38:04 +01:00
parent 3c58851b88
commit b29e4edd72
Signed by: lukas
SSH Key Fingerprint: SHA256:vMuSwvwAvcT6htVAioMP7rzzwMQNi3roESyhv+nAxeg
4 changed files with 45 additions and 50 deletions

View File

@ -13,7 +13,7 @@ durationformat_manual: str = "%H hours"
def _safe_timedelta(duration: timedelta | int | None): def _safe_timedelta(duration: timedelta | int | None):
if duration == None: if duration is None:
return timedelta(0) return timedelta(0)
elif isinstance(duration, int): elif isinstance(duration, int):
return timedelta(seconds=duration) return timedelta(seconds=duration)

View File

@ -1,5 +1,6 @@
<c-layouts.base> <c-layouts.base>
{% load static %} {% load static %}
{% load duration_formatter %}
{% partialdef purchase-name %} {% partialdef purchase-name %}
{% if purchase.type != 'game' %} {% if purchase.type != 'game' %}
<c-gamelink :game_id=purchase.first_game.id> <c-gamelink :game_id=purchase.first_game.id>
@ -111,7 +112,7 @@
{% for month in month_playtimes %} {% for month in month_playtimes %}
<tr> <tr>
<td class="px-2 sm:px-4 md:px-6 md:py-2">{{ month.month | date:"F" }}</td> <td class="px-2 sm:px-4 md:px-6 md:py-2">{{ month.month | date:"F" }}</td>
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ month.playtime }}</td> <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ month.playtime | format_duration }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -157,7 +158,7 @@
<thead> <thead>
<tr> <tr>
<th class="px-2 sm:px-4 md:px-6 md:py-2">Name</th> <th class="px-2 sm:px-4 md:px-6 md:py-2">Name</th>
<th class="px-2 sm:px-4 md:px-6 md:py-2">Playtime (hours)</th> <th class="px-2 sm:px-4 md:px-6 md:py-2">Playtime</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -166,7 +167,7 @@
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono"> <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">
<c-gamelink :game_id=game.id :name=game.name /> <c-gamelink :game_id=game.id :name=game.name />
</td> </td>
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ game.formatted_playtime }}</td> <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ game.playtime | format_duration }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -176,14 +177,14 @@
<thead> <thead>
<tr> <tr>
<th class="px-2 sm:px-4 md:px-6 md:py-2">Platform</th> <th class="px-2 sm:px-4 md:px-6 md:py-2">Platform</th>
<th class="px-2 sm:px-4 md:px-6 md:py-2">Playtime (hours)</th> <th class="px-2 sm:px-4 md:px-6 md:py-2">Playtime</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for item in total_playtime_per_platform %} {% for item in total_playtime_per_platform %}
<tr> <tr>
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ item.platform_name }}</td> <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ item.platform_name }}</td>
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ item.formatted_playtime }}</td> <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ item.playtime | format_duration }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -213,14 +214,14 @@
<thead> <thead>
<tr> <tr>
<th class="px-2 sm:px-4 md:px-6 md:py-2 purchase-name truncate max-w-20char">Name</th> <th class="px-2 sm:px-4 md:px-6 md:py-2 purchase-name truncate max-w-20char">Name</th>
{% comment %} <th class="px-2 sm:px-4 md:px-6 md:py-2">Date</th> {% endcomment %} <th class="px-2 sm:px-4 md:px-6 md:py-2">Date</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for purchase in this_year_finished_this_year %} {% for purchase in this_year_finished_this_year %}
<tr> <tr>
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{% partial purchase-name %}</td> <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{% partial purchase-name %}</td>
{% comment %} <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ purchase.date_finished | date:"d/m/Y" }}</td> {% endcomment %} <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ purchase.date_finished | date:"d/m/Y" }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -232,14 +233,14 @@
<thead> <thead>
<tr> <tr>
<th class="px-2 sm:px-4 md:px-6 md:py-2 purchase-name truncate max-w-20char">Name</th> <th class="px-2 sm:px-4 md:px-6 md:py-2 purchase-name truncate max-w-20char">Name</th>
{% comment %} <th class="px-2 sm:px-4 md:px-6 md:py-2">Date</th> {% endcomment %} <th class="px-2 sm:px-4 md:px-6 md:py-2">Date</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for purchase in purchased_this_year_finished_this_year %} {% for purchase in purchased_this_year_finished_this_year %}
<tr> <tr>
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{% partial purchase-name %}</td> <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{% partial purchase-name %}</td>
{% comment %} <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ purchase.date_finished | date:"d/m/Y" }}</td> {% endcomment %} <td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ purchase.date_finished | date:"d/m/Y" }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -0,0 +1,12 @@
from datetime import timedelta
from django import template
from common.time import durationformat, format_duration
register = template.Library()
@register.filter(name="format_duration")
def filter_format_duration(duration: timedelta, argument: str = durationformat):
return format_duration(duration, format_string=argument)

View File

@ -22,10 +22,10 @@ def model_counts(request: HttpRequest) -> dict[str, bool]:
timestamp_start__day=this_day, timestamp_start__day=this_day,
timestamp_start__month=this_month, timestamp_start__month=this_month,
timestamp_start__year=this_year, timestamp_start__year=this_year,
).aggregate(time=Sum(F("duration_calculated") + F("duration_manual")))["time"] ).aggregate(time=Sum(F("duration_total")))["time"]
last_7_played = Session.objects.filter( last_7_played = Session.objects.filter(
timestamp_start__gte=(now - timedelta(days=7)) timestamp_start__gte=(now - timedelta(days=7))
).aggregate(time=Sum(F("duration_calculated") + F("duration_manual")))["time"] ).aggregate(time=Sum(F("duration_total")))["time"]
return { return {
"game_available": Game.objects.exists(), "game_available": Game.objects.exists(),
@ -137,19 +137,13 @@ def stats_alltime(request: HttpRequest) -> HttpResponse:
) )
total_spent = this_year_spendings["total_spent"] or 0 total_spent = this_year_spendings["total_spent"] or 0
games_with_playtime = ( games_with_playtime = Game.objects.filter(
Game.objects.filter(sessions__in=this_year_sessions) sessions__in=this_year_sessions
.annotate( ).distinct()
total_playtime=Sum(
F("sessions__duration_calculated") + F("sessions__duration_manual")
)
)
.values("id", "name", "total_playtime")
)
month_playtimes = ( month_playtimes = (
this_year_sessions.annotate(month=TruncMonth("timestamp_start")) this_year_sessions.annotate(month=TruncMonth("timestamp_start"))
.values("month") .values("month")
.annotate(playtime=Sum("duration_calculated")) .annotate(playtime=Sum("duration_total"))
.order_by("month") .order_by("month")
) )
for month in month_playtimes: for month in month_playtimes:
@ -162,18 +156,14 @@ def stats_alltime(request: HttpRequest) -> HttpResponse:
.first() .first()
) )
top_10_games_by_playtime = games_with_playtime.order_by("-total_playtime")[:10] top_10_games_by_playtime = games_with_playtime.order_by("-total_playtime")[:10]
for game in top_10_games_by_playtime:
game["formatted_playtime"] = format_duration(game["total_playtime"], "%2.0H")
total_playtime_per_platform = ( total_playtime_per_platform = (
this_year_sessions.values("game__platform__name") this_year_sessions.values("game__platform__name")
.annotate(total_playtime=Sum(F("duration_calculated") + F("duration_manual"))) .annotate(playtime=Sum(F("duration_total")))
.annotate(platform_name=F("game__platform__name")) .annotate(platform_name=F("game__platform__name"))
.values("platform_name", "total_playtime") .values("platform_name", "playtime")
.order_by("-total_playtime") .order_by("-playtime")
) )
for item in total_playtime_per_platform:
item["formatted_playtime"] = format_duration(item["total_playtime"], "%2.0H")
backlog_decrease_count = ( backlog_decrease_count = (
Purchase.objects.all().intersection(purchases_finished_this_year).count() Purchase.objects.all().intersection(purchases_finished_this_year).count()
@ -362,7 +352,11 @@ def stats(request: HttpRequest, year: int = 0) -> HttpResponse:
) )
) )
purchased_this_year_finished_this_year = ( purchased_this_year_finished_this_year = (
this_year_purchases_without_refunded.filter(games__playevents__ended__year=year) this_year_purchases_without_refunded.filter(
games__playevents__ended__year=year
).annotate(
game_name=F("games__name"), date_finished=F("games__playevents__ended")
)
).order_by("games__playevents__ended") ).order_by("games__playevents__ended")
this_year_spendings = this_year_purchases_without_refunded.aggregate( this_year_spendings = this_year_purchases_without_refunded.aggregate(
@ -370,23 +364,15 @@ def stats(request: HttpRequest, year: int = 0) -> HttpResponse:
) )
total_spent = this_year_spendings["total_spent"] or 0 total_spent = this_year_spendings["total_spent"] or 0
games_with_playtime = ( games_with_playtime = Game.objects.filter(
Game.objects.filter(sessions__in=this_year_sessions) sessions__in=this_year_sessions
.annotate( ).distinct()
total_playtime=Sum(
F("sessions__duration_calculated") + F("sessions__duration_manual")
)
)
.values("id", "name", "total_playtime")
)
month_playtimes = ( month_playtimes = (
this_year_sessions.annotate(month=TruncMonth("timestamp_start")) this_year_sessions.annotate(month=TruncMonth("timestamp_start"))
.values("month") .values("month")
.annotate(playtime=Sum("duration_calculated")) .annotate(playtime=Sum("duration_total"))
.order_by("month") .order_by("month")
) )
for month in month_playtimes:
month["playtime"] = format_duration(month["playtime"], "%2.0H")
highest_session_average_game = ( highest_session_average_game = (
Game.objects.filter(sessions__in=this_year_sessions) Game.objects.filter(sessions__in=this_year_sessions)
@ -394,19 +380,15 @@ def stats(request: HttpRequest, year: int = 0) -> HttpResponse:
.order_by("-session_average") .order_by("-session_average")
.first() .first()
) )
top_10_games_by_playtime = games_with_playtime.order_by("-total_playtime") top_10_games_by_playtime = games_with_playtime.order_by("-playtime")
for game in top_10_games_by_playtime:
game["formatted_playtime"] = format_duration(game["total_playtime"], "%2.0H")
total_playtime_per_platform = ( total_playtime_per_platform = (
this_year_sessions.values("game__platform__name") this_year_sessions.values("game__platform__name")
.annotate(total_playtime=Sum(F("duration_calculated") + F("duration_manual"))) .annotate(playtime=Sum(F("duration_total")))
.annotate(platform_name=F("game__platform__name")) .annotate(platform_name=F("game__platform__name"))
.values("platform_name", "total_playtime") .values("platform_name", "playtime")
.order_by("-total_playtime") .order_by("-playtime")
) )
for item in total_playtime_per_platform:
item["formatted_playtime"] = format_duration(item["total_playtime"], "%2.0H")
backlog_decrease_count = ( backlog_decrease_count = (
Purchase.objects.filter(date_purchased__year__lt=year) Purchase.objects.filter(date_purchased__year__lt=year)