147 lines
4.7 KiB
Python
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
|