From 50b10689fc1282f8a50142f6710b4ac59807f3e0 Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Thu, 4 Jun 2026 22:01:35 -0400 Subject: [PATCH] [scrobbles] Add total time to tag views --- PROJECT.org | 12 ++++++- vrobbler/apps/scrobbles/views.py | 10 +++++- .../templatetags/naturalduration.py | 33 +++++++++---------- .../scrobbles/scrobble_all_list.html | 3 ++ 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/PROJECT.org b/PROJECT.org index fbb3f90..0ead160 100644 --- a/PROJECT.org +++ b/PROJECT.org @@ -93,7 +93,7 @@ fetching and simple saving. :LOGBOOK: CLOCK: [2025-07-09 Wed 09:55]--[2025-07-09 Wed 10:15] => 0:20 :END: -* Backlog [3/17] :vrobbler:project:personal: +* Backlog [4/18] :vrobbler:project:personal: ** TODO [#C] Add sentiment parsing for Scrobbles with notes :vrobbler:project:scrobbles:sentiment: :PROPERTIES: :ID: 37781d6a-f3b0-48b2-bf98-33c2c791cf85 @@ -503,6 +503,16 @@ The graph would contain all Weigh-in scrobbles for that user, no matter which date is being viewed, and the highlighted value on the graph would be the date being viewed. +** DONE [#B] When viewing scrobbles by tag, sum the total time :scrobbles:tags: +:PROPERTIES: +:ID: d51f23df-c2c5-4e1a-b000-67c89032af02 +:END: + +*** Description + +On scrobbles filtered by tags, we should see a sum of the time spent doing those tasks, in a human +readable format like "X days, X hours, X minutes and X seconds" + ** DONE [#A] Orgmode tasks are not updated if in progress :tasks:orgmode:bug: :PROPERTIES: :ID: 7dcebb2c-7c4c-4ac5-bee6-c2e36c3811f9 diff --git a/vrobbler/apps/scrobbles/views.py b/vrobbler/apps/scrobbles/views.py index 3b7fd89..5015c92 100644 --- a/vrobbler/apps/scrobbles/views.py +++ b/vrobbler/apps/scrobbles/views.py @@ -14,7 +14,7 @@ from django.contrib import messages from django.contrib.auth import get_user_model from django.contrib.auth.mixins import LoginRequiredMixin from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator -from django.db.models import Count, Max, Q +from django.db.models import Count, Max, Q, Sum from django.db.models.query import QuerySet from rest_framework.authentication import TokenAuthentication from rest_framework.authtoken.models import Token @@ -367,6 +367,7 @@ class ScrobbleListView(LoginRequiredMixin, ListView): else: tag_list = [] self.tag_list = tag_list + self._full_queryset = qs return qs def _compute_overlap_groups(self, scrobbles): @@ -433,6 +434,13 @@ class ScrobbleListView(LoginRequiredMixin, ListView): ctx["tag_list"] = getattr(self, "tag_list", []) scrobbles = list(ctx.get("object_list", [])) ctx["overlap_map"] = self._compute_overlap_groups(scrobbles) + full_qs = getattr(self, "_full_queryset", None) + if full_qs is not None and getattr(self, "tag_list", []): + total = ( + full_qs.aggregate(total=Sum("playback_position_seconds"))["total"] + or 0 + ) + ctx["total_time_seconds"] = total return ctx diff --git a/vrobbler/apps/videogames/templatetags/naturalduration.py b/vrobbler/apps/videogames/templatetags/naturalduration.py index 075a2a2..c1b276f 100644 --- a/vrobbler/apps/videogames/templatetags/naturalduration.py +++ b/vrobbler/apps/videogames/templatetags/naturalduration.py @@ -8,21 +8,20 @@ def natural_duration(value): if not value: return value = int(value) - total_minutes = int(value / 60) - hours = int(total_minutes / 60) - minutes = total_minutes - (hours * 60) - seconds = value % 60 - value_str = "" - if seconds: - value_str = f"{seconds} seconds" - if minutes: - if value_str: - value_str = f"{minutes} minutes, " + value_str - else: - value_str = f"{minutes} minutes" + days = int(value / 86400) + remainder = value % 86400 + hours = int(remainder / 3600) + minutes = int((remainder % 3600) / 60) + seconds = remainder % 60 + parts = [] + if days: + parts.append(f"{days} day{'s' if days != 1 else ''}") if hours: - if value_str: - value_str = f"{hours} hours, " + value_str - else: - value_str = f"{hours} hours" - return value_str + parts.append(f"{hours} hour{'s' if hours != 1 else ''}") + if minutes: + parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}") + if seconds or not parts: + parts.append(f"{seconds} second{'s' if seconds != 1 else ''}") + if len(parts) == 1: + return parts[0] + return ", ".join(parts[:-1]) + " and " + parts[-1] diff --git a/vrobbler/templates/scrobbles/scrobble_all_list.html b/vrobbler/templates/scrobbles/scrobble_all_list.html index 4851d91..bc89105 100644 --- a/vrobbler/templates/scrobbles/scrobble_all_list.html +++ b/vrobbler/templates/scrobbles/scrobble_all_list.html @@ -9,6 +9,9 @@

All Scrobbles

{% if tag_list %}
Tagged {{ tag_list|join:", " }}
+ {% if total_time_seconds %} +

Total time: {{ total_time_seconds|natural_duration }}

+ {% endif %} {% endif %}