From 6471413681e09a42025bc9c0e6fad5e585eba3c5 Mon Sep 17 00:00:00 2001
From: Colin Powell
Date: Thu, 4 Jun 2026 22:13:14 -0400
Subject: [PATCH] [tasks] Add weigh in graph
---
PROJECT.org | 8 ++-
vrobbler/apps/tasks/views.py | 68 +++++++++++++++++++++++
vrobbler/templates/tasks/task_detail.html | 68 +++++++++++++++++++++++
3 files changed, 142 insertions(+), 2 deletions(-)
diff --git a/PROJECT.org b/PROJECT.org
index 0ead160..d332ac5 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 [4/18] :vrobbler:project:personal:
+* Backlog [5/18] :vrobbler:project:personal:
** TODO [#C] Add sentiment parsing for Scrobbles with notes :vrobbler:project:scrobbles:sentiment:
:PROPERTIES:
:ID: 37781d6a-f3b0-48b2-bf98-33c2c791cf85
@@ -489,7 +489,7 @@ whatever time KoReader reports, we need to know, given the date and the user
profile's historic timezone, how many hours to adjust the KoReader time to get
to GMT to save it in the database.
-** TODO [#B] Can we show a graph of all past Weigh-in tasks :scale:tasks:graphs:javascript:
+** DONE [#B] Can we show a graph of all past Weigh-in tasks :scale:tasks:graphs:javascript:
:PROPERTIES:
:ID: ae499d87-03bf-4e48-9b2c-1a421a46af11
:END:
@@ -503,6 +503,10 @@ 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.
+Probably could use something like chart.js although maybe that's too heavy?
+
+And can we have each metric overlayed on the same graph?
+
** DONE [#B] When viewing scrobbles by tag, sum the total time :scrobbles:tags:
:PROPERTIES:
:ID: d51f23df-c2c5-4e1a-b000-67c89032af02
diff --git a/vrobbler/apps/tasks/views.py b/vrobbler/apps/tasks/views.py
index dcecad5..989ec88 100644
--- a/vrobbler/apps/tasks/views.py
+++ b/vrobbler/apps/tasks/views.py
@@ -1,5 +1,6 @@
import logging
+import pendulum
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
@@ -8,6 +9,7 @@ from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
+from scrobbles.models import Scrobble
from scrobbles.views import ScrobbleableDetailView, ScrobbleableListView
from tasks.models import Task
@@ -23,6 +25,72 @@ class TaskListView(ScrobbleableListView):
class TaskDetailView(ScrobbleableDetailView):
model = Task
+ def get_context_data(self, **kwargs):
+ ctx = super().get_context_data(**kwargs)
+ if self.object.title != "Weigh-in":
+ return ctx
+
+ scrobbles = list(
+ Scrobble.objects.filter(
+ user=self.request.user,
+ task=self.object,
+ log__weight__isnull=False,
+ ).order_by("timestamp")
+ )
+ if not scrobbles:
+ return ctx
+
+ labels = []
+ weight_data = []
+ body_fat_data = []
+ bmi_data = []
+ for s in scrobbles:
+ ts = s.timestamp
+ if isinstance(ts, str):
+ ts = pendulum.parse(ts)
+ labels.append(ts.strftime("%Y-%m-%d"))
+ log = s.log if isinstance(s.log, dict) else {}
+ raw_weight = log.get("weight")
+ weight_data.append(
+ float(raw_weight) if raw_weight is not None else None
+ )
+ raw_bf = log.get("body_fat")
+ body_fat_data.append(
+ float(raw_bf) if raw_bf is not None else None
+ )
+ raw_bmi = log.get("bmi")
+ bmi_data.append(
+ float(raw_bmi) if raw_bmi is not None else None
+ )
+
+ ctx["weighin_chart"] = {
+ "labels": labels,
+ "datasets": [
+ {
+ "label": "Weight",
+ "data": weight_data,
+ "borderColor": "#4bc0c0",
+ "fill": False,
+ "yAxisID": "y",
+ },
+ {
+ "label": "Body Fat %",
+ "data": body_fat_data,
+ "borderColor": "#ff6384",
+ "fill": False,
+ "yAxisID": "y1",
+ },
+ {
+ "label": "BMI",
+ "data": bmi_data,
+ "borderColor": "#36a2eb",
+ "fill": False,
+ "yAxisID": "y2",
+ },
+ ],
+ }
+ return ctx
+
@api_view(["GET"])
@permission_classes([IsAuthenticated])
diff --git a/vrobbler/templates/tasks/task_detail.html b/vrobbler/templates/tasks/task_detail.html
index e5798fd..05dd1ea 100644
--- a/vrobbler/templates/tasks/task_detail.html
+++ b/vrobbler/templates/tasks/task_detail.html
@@ -2,6 +2,7 @@
{% load mathfilters %}
{% load static %}
{% load naturalduration %}
+{% load l10n %}
{% block title %}{{object.title}}{% endblock %}
@@ -39,6 +40,15 @@
+
+{% if weighin_chart %}
+
+{% endif %}
+
{{scrobbles.count}} scrobbles
@@ -94,3 +104,61 @@
{% endif %}
{% endblock %}
+
+{% block extra_js %}
+{% if weighin_chart %}
+
+
+{% endif %}
+{% endblock %}