Files
vrobbler/vrobbler/apps/trends/views.py
Colin Powell 471e70ff7f
Some checks failed
build / test (push) Has been cancelled
[trends] Remove all time trends
2026-06-17 17:09:11 -04:00

147 lines
4.7 KiB
Python

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
from trends.models import TrendResult
from trends.trends import TREND_REGISTRY
from trends.utils import get_period_nav, get_supported_periods
TREND_METADATA = {
"activity-distribution": {
"title": "Activity Distribution",
"description": "How your scrobbles are divided across media types.",
"icon": "📊",
},
"concurrent-listening": {
"title": "Concurrent Listening",
"description": "What music were you listening to while on trails or at locations?",
"icon": "🎧",
},
"concurrent-reading": {
"title": "Concurrent Reading",
"description": "What music did you listen to while reading books?",
"icon": "📖",
},
"mood-trajectory": {
"title": "Mood Trajectory",
"description": "How your mood quality has changed over time.",
"icon": "📈",
},
"mood-by-time": {
"title": "Mood by Time",
"description": "How your mood varies by hour of day and day of week.",
"icon": "🕐",
},
"mood-distribution": {
"title": "Mood Distribution",
"description": "Which moods you feel most often.",
"icon": "🎭",
},
"mood-streaks": {
"title": "Mood Streaks",
"description": "Your longest runs of positive and negative moods.",
"icon": "🔥",
},
"mood-weather": {
"title": "Mood & Weather",
"description": "How weather conditions correlate with your mood.",
"icon": "🌤",
},
"peak-hours": {
"title": "Peak Activity Hours",
"description": "What time of day are you most active?",
"icon": "🕐",
},
"reading-pace-vs-activity": {
"title": "Reading Pace vs Music",
"description": "Compare how long you read per session with and without concurrent music.",
"icon": "📊",
},
"trending-up": {
"title": "Trending Media Types",
"description": "Which media types have you been consuming more or less of recently?",
"icon": "📈",
},
"weekly-rhythm": {
"title": "Weekly Rhythm",
"description": "Which days of the week see the most scrobble activity?",
"icon": "📅",
},
}
class TrendListView(LoginRequiredMixin, TemplateView):
template_name = "trends/trend_list.html"
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
results = TrendResult.objects.filter(
user=self.request.user,
).order_by("trend_slug", "-computed_at")
latest_by_slug = {}
for r in results:
if r.trend_slug not in latest_by_slug:
latest_by_slug[r.trend_slug] = r
trends = []
for slug in TREND_REGISTRY:
meta = TREND_METADATA.get(slug, {})
result = latest_by_slug.get(slug)
trends.append(
{
"slug": slug,
"title": meta.get("title", slug),
"description": meta.get("description", ""),
"icon": meta.get("icon", ""),
"computed_at": result.computed_at if result else None,
"has_data": result is not None,
}
)
ctx["trends"] = trends
return ctx
class TrendDetailView(LoginRequiredMixin, TemplateView):
template_name = "trends/trend_detail.html"
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
slug = kwargs["trend_slug"]
if slug not in TREND_REGISTRY:
ctx["trend_not_found"] = True
return ctx
period = self.request.GET.get("period", "last_30")
meta = TREND_METADATA.get(slug, {})
ctx["trend"] = {
"slug": slug,
"title": meta.get("title", slug),
"description": meta.get("description", ""),
"icon": meta.get("icon", ""),
}
supported = get_supported_periods(slug)
ctx["supported_periods"] = supported
ctx["current_period"] = period
ctx["current_period_label"] = supported.get(period, "")
prev_period, next_period = get_period_nav(period, slug)
ctx["prev_period"] = prev_period
ctx["next_period"] = next_period
result = TrendResult.objects.filter(
user=self.request.user,
trend_slug=slug,
period=period,
).first()
if result:
ctx["computed_at"] = result.computed_at
ctx["data"] = result.data
else:
ctx["computed_at"] = None
ctx["data"] = None
return ctx