Files
vrobbler/vrobbler/apps/trends/utils.py
Colin Powell 327ba94c63
All checks were successful
build / test (push) Successful in 2m4s
[trends] Add new time of day trend
2026-06-23 22:21:18 -04:00

86 lines
2.1 KiB
Python

import logging
from datetime import timedelta
from django.utils import timezone
from trends.models import PERIOD_CHOICES, TrendResult
logger = logging.getLogger(__name__)
PERIOD_DAYS = {
"last_30": 30,
"last_90": 90,
"last_year": 365,
}
PERIOD_LABELS = dict(PERIOD_CHOICES)
TIME_BOUND_TRENDS = {
"activity-distribution",
"concurrent-reading",
"concurrent-listening",
"mood-by-time",
"mood-distribution",
"mood-streaks",
"mood-trajectory",
"mood-weather",
"peak-hours",
"reading-pace-vs-activity",
"time-of-day-categories",
"trending-up",
"weekly-rhythm",
}
TREND_PERIOD_OVERRIDES = {
"trending-up": ["last_30", "last_90", "last_year"],
}
def get_supported_periods(trend_slug):
if trend_slug in TREND_PERIOD_OVERRIDES:
slugs = TREND_PERIOD_OVERRIDES[trend_slug]
return {s: PERIOD_LABELS[s] for s in slugs}
return dict(PERIOD_LABELS)
def get_period_days(period):
return PERIOD_DAYS.get(period)
def get_date_range(period):
days = get_period_days(period)
if days is None:
return None, None
now = timezone.now()
return now - timedelta(days=days), now
def get_period_nav(current_period, trend_slug):
supported = get_supported_periods(trend_slug)
keys = list(supported.keys())
try:
idx = keys.index(current_period)
except ValueError:
return None, None
prev_period = keys[idx - 1] if idx > 0 else None
next_period = keys[idx + 1] if idx < len(keys) - 1 else None
return prev_period, next_period
def compute_and_save_trend(user, slug, period="last_30"):
"""Compute a single trend for a given period and persist the result.
Returns elapsed seconds on success, raises on failure.
"""
from trends.trends import TREND_REGISTRY
fn = TREND_REGISTRY[slug]
start = timezone.now()
data = fn(user, period=period)
TrendResult.objects.update_or_create(
user=user,
trend_slug=slug,
period=period,
defaults={"data": data, "computed_at": timezone.now()},
)
return (timezone.now() - start).total_seconds()