diff --git a/vrobbler/apps/scrobbles/utils.py b/vrobbler/apps/scrobbles/utils.py index ee548b7..ac86271 100644 --- a/vrobbler/apps/scrobbles/utils.py +++ b/vrobbler/apps/scrobbles/utils.py @@ -1,15 +1,18 @@ import hashlib import logging import re -from datetime import datetime, timedelta +from datetime import date, datetime, timedelta from typing import TYPE_CHECKING, Optional from urllib.parse import urlparse from zoneinfo import ZoneInfo +import pendulum import pytz from django.apps import apps from django.contrib.auth import get_user_model from django.db import models +from django.db.models.fields.json import KeyTextTransform +from django.db.models.functions import Cast, TruncDate from django.utils import timezone from profiles.models import UserProfile from profiles.utils import now_user_timezone @@ -371,3 +374,37 @@ def fix_playback_position_seconds(*scrobbles: "Scrobble", commit=True) -> list[" scrobble.save(update_fields=["playback_position_seconds"]) return updated_scrobbles + + +def base_scrobble_qs(user_id: int) -> models.QuerySet: + """Base queryset with calories extracted as integer and day annotated.""" + from scrobbles.models import Scrobble + + return ( + Scrobble.objects + .annotate(day=TruncDate("timestamp")) + .annotate(calories_int=Cast(KeyTextTransform("calories", "log"), models.IntegerField())) + .filter(calories_int__isnull=False, user_id=user_id) + ) + +def get_daily_calories_for_user_by_day(user_id: int, date: date| str) -> int: + """Return total calories for a user on a specific day.""" + + if isinstance(date, str): + date = pendulum.parse(date) + + qs = base_scrobble_qs(user_id).filter(day=date) + agg = qs.aggregate(total_calories=models.Sum("calories_int")) + + return agg["total_calories"] or 0 + +def get_daily_calorie_dict_for_user(user_id: int) -> dict[date, int]: + """Return {day: total_calories} for all days with scrobbles, in one query.""" + qs = ( + base_scrobble_qs(user_id) + .values("day") + .annotate(total_calories=models.Sum("calories_int")) + .order_by("day") + ) + + return {entry["day"]: entry["total_calories"] for entry in qs} diff --git a/vrobbler/apps/scrobbles/views.py b/vrobbler/apps/scrobbles/views.py index 23490da..b435c9e 100644 --- a/vrobbler/apps/scrobbles/views.py +++ b/vrobbler/apps/scrobbles/views.py @@ -2,26 +2,27 @@ import calendar import json import logging from datetime import datetime, timedelta -from dateutil.relativedelta import relativedelta -from django.shortcuts import redirect import pendulum import pytz +from dateutil.relativedelta import relativedelta from django.apps import apps from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin -from django.db.models import Count, Q, Max +from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator +from django.db.models import Count, Max, Q from django.db.models.query import QuerySet from django.http import FileResponse, HttpResponseRedirect, JsonResponse +from django.shortcuts import redirect from django.urls import reverse_lazy from django.utils import timezone from django.views.decorators.csrf import csrf_exempt from django.views.generic import DetailView, FormView, TemplateView from django.views.generic.edit import CreateView from django.views.generic.list import ListView +from moods.models import Mood from music.aggregators import live_charts, scrobble_counts, week_of_scrobbles - -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from pendulum.parsing.exceptions import ParserError from rest_framework import status from rest_framework.decorators import ( api_view, @@ -54,10 +55,10 @@ from scrobbles.tasks import ( process_tsv_import, ) from scrobbles.utils import ( + get_daily_calories_for_user_by_day, get_long_plays_completed, get_long_plays_in_progress, ) -from moods.models import Mood logger = logging.getLogger(__name__) @@ -146,7 +147,7 @@ class RecentScrobbleList(ListView): if date_str: try: date = pendulum.parse(date_str) - except: + except ParserError: pass if date_str: if date_str == "this_week" or "-W" in date_str: @@ -230,6 +231,7 @@ class RecentScrobbleList(ListView): user=self.request.user, ) data["counts"] = [] # scrobble_counts(user) + data["daily_calories"] = get_daily_calories_for_user_by_day(self.request.user.id, date) else: data["weekly_data"] = week_of_scrobbles() data["counts"] = scrobble_counts() diff --git a/vrobbler/templates/scrobbles/_scrobble_table.html b/vrobbler/templates/scrobbles/_scrobble_table.html index f9f1d0c..0282880 100644 --- a/vrobbler/templates/scrobbles/_scrobble_table.html +++ b/vrobbler/templates/scrobbles/_scrobble_table.html @@ -4,7 +4,7 @@