浏览代码

[food] Add calories per day

Colin Powell 4 周之前
父节点
当前提交
0b4537b7ed

+ 38 - 1
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}

+ 9 - 7
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()

+ 1 - 1
vrobbler/templates/scrobbles/_scrobble_table.html

@@ -4,7 +4,7 @@
 <div class="tab-pane fade show" id="latest-beers" role="tabpanel"
     aria-labelledby="latest-beers-tab">
     <div class="table-responsive">
-        {{count}} scrobble {% if time %}| {{time|natural_duration}}{% endif %}
+        {{count}} scrobble {% if time %}| {{time|natural_duration}}{% endif %}{% if daily_calories %}| {{daily_calories}} calories{% endif %}
         <table class="table table-striped table-sm">
             <thead>
                 <tr>