[scrobbles] Add total time to tag views
This commit is contained in:
12
PROJECT.org
12
PROJECT.org
@ -93,7 +93,7 @@ fetching and simple saving.
|
|||||||
:LOGBOOK:
|
:LOGBOOK:
|
||||||
CLOCK: [2025-07-09 Wed 09:55]--[2025-07-09 Wed 10:15] => 0:20
|
CLOCK: [2025-07-09 Wed 09:55]--[2025-07-09 Wed 10:15] => 0:20
|
||||||
:END:
|
: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:
|
** TODO [#C] Add sentiment parsing for Scrobbles with notes :vrobbler:project:scrobbles:sentiment:
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:ID: 37781d6a-f3b0-48b2-bf98-33c2c791cf85
|
: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
|
date is being viewed, and the highlighted value on the graph would be the date
|
||||||
being viewed.
|
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:
|
** DONE [#A] Orgmode tasks are not updated if in progress :tasks:orgmode:bug:
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:ID: 7dcebb2c-7c4c-4ac5-bee6-c2e36c3811f9
|
: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 import get_user_model
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
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 django.db.models.query import QuerySet
|
||||||
from rest_framework.authentication import TokenAuthentication
|
from rest_framework.authentication import TokenAuthentication
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
@ -367,6 +367,7 @@ class ScrobbleListView(LoginRequiredMixin, ListView):
|
|||||||
else:
|
else:
|
||||||
tag_list = []
|
tag_list = []
|
||||||
self.tag_list = tag_list
|
self.tag_list = tag_list
|
||||||
|
self._full_queryset = qs
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def _compute_overlap_groups(self, scrobbles):
|
def _compute_overlap_groups(self, scrobbles):
|
||||||
@ -433,6 +434,13 @@ class ScrobbleListView(LoginRequiredMixin, ListView):
|
|||||||
ctx["tag_list"] = getattr(self, "tag_list", [])
|
ctx["tag_list"] = getattr(self, "tag_list", [])
|
||||||
scrobbles = list(ctx.get("object_list", []))
|
scrobbles = list(ctx.get("object_list", []))
|
||||||
ctx["overlap_map"] = self._compute_overlap_groups(scrobbles)
|
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
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -8,21 +8,20 @@ def natural_duration(value):
|
|||||||
if not value:
|
if not value:
|
||||||
return
|
return
|
||||||
value = int(value)
|
value = int(value)
|
||||||
total_minutes = int(value / 60)
|
days = int(value / 86400)
|
||||||
hours = int(total_minutes / 60)
|
remainder = value % 86400
|
||||||
minutes = total_minutes - (hours * 60)
|
hours = int(remainder / 3600)
|
||||||
seconds = value % 60
|
minutes = int((remainder % 3600) / 60)
|
||||||
value_str = ""
|
seconds = remainder % 60
|
||||||
if seconds:
|
parts = []
|
||||||
value_str = f"{seconds} seconds"
|
if days:
|
||||||
if minutes:
|
parts.append(f"{days} day{'s' if days != 1 else ''}")
|
||||||
if value_str:
|
|
||||||
value_str = f"{minutes} minutes, " + value_str
|
|
||||||
else:
|
|
||||||
value_str = f"{minutes} minutes"
|
|
||||||
if hours:
|
if hours:
|
||||||
if value_str:
|
parts.append(f"{hours} hour{'s' if hours != 1 else ''}")
|
||||||
value_str = f"{hours} hours, " + value_str
|
if minutes:
|
||||||
else:
|
parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}")
|
||||||
value_str = f"{hours} hours"
|
if seconds or not parts:
|
||||||
return value_str
|
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>
|
<h1 class="h2">All Scrobbles</h1>
|
||||||
{% if tag_list %}
|
{% if tag_list %}
|
||||||
<h6 class="text-muted">Tagged {{ tag_list|join:", " }}</h6>
|
<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 %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user