This commit has no recorded session.
diff
commit 50b10689fc1282f8a50142f6710b4ac59807f3e0Author: Colin Powell <colin@unbl.ink>Date: Thu Jun 4 22:01:35 2026 -0400 [scrobbles] Add total time to tag views@@ -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@@ -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 @@ -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]@@ -9,6 +9,9 @@ <h1 class="h2">All Scrobbles</h1> {% if tag_list %} <h6 class="text-muted">Tagged {{ tag_list|join:", " }}</h6>+ {% if total_time_seconds %}+ <p class="text-muted small mb-0">Total time: {{ total_time_seconds|natural_duration }}</p>+ {% endif %} {% endif %} </div> </div>